适配器模式与外观模式:让不兼容的接口和谐共处!
大家好!今天我们来聊聊设计模式中的适配器模式 (Adapter Pattern)和外观模式 (Facade Pattern)。如果你曾经遇到过接口不兼容的问题,或者希望简化复杂系统的调用方式,那么这两种模式就是你的救星!本文基于《Head First 设计模式》的适配器模式与外观模式章节,通过生动的故事和 Java 代码示例,带你轻松掌握这两种模式的精髓。

1. 适配器模式:让不兼容的接口和谐共处
故事背景
小明开发了一个音乐播放器系统,系统中有一个 MediaPlayer 接口,用于播放音频文件。现有的实现类 AudioPlayer 可以播放 MP3 文件,但无法播放 MP4 和 VLC 文件。
问题出现
小明希望扩展系统,使其支持播放 MP4 和 VLC 文件。然而,MP4 和 VLC 文件的播放逻辑由第三方库提供,接口与 MediaPlayer 不兼容。
解决方案:适配器模式
小明决定使用适配器模式,将第三方库的接口适配成 MediaPlayer 接口,从而在不修改现有代码的情况下扩展系统功能。
代码实现
1. 定义目标接口
java
// 目标接口:MediaPlayer
interface MediaPlayer {
void play(String audioType, String fileName);
}
2. 实现现有类
java
// 现有类:AudioPlayer
class AudioPlayer implements MediaPlayer {
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("mp3")) {
System.out.println("Playing MP3 file: " + fileName);
} else {
System.out.println("Unsupported audio type: " + audioType);
}
}
}
3. 定义第三方接口
java
// 第三方接口:AdvancedMediaPlayer
interface AdvancedMediaPlayer {
void playMp4(String fileName);
void playVlc(String fileName);
}
// 具体实现类:Mp4Player
class Mp4Player implements AdvancedMediaPlayer {
@Override
public void playMp4(String fileName) {
System.out.println("Playing MP4 file: " + fileName);
}
@Override
public void playVlc(String fileName) {
// Do nothing
}
}
// 具体实现类:VlcPlayer
class VlcPlayer implements AdvancedMediaPlayer {
@Override
public void playMp4(String fileName) {
// Do nothing
}
@Override
public void playVlc(String fileName) {
System.out.println("Playing VLC file: " + fileName);
}
}
4. 实现适配器
java
// 适配器类:MediaAdapter
class MediaAdapter implements MediaPlayer {
private AdvancedMediaPlayer advancedMediaPlayer;
public MediaAdapter(String audioType) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedMediaPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMediaPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedMediaPlayer.playVlc(fileName);
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMediaPlayer.playMp4(fileName);
}
}
}
5. 扩展现有类
java
// 扩展后的 AudioPlayer
class AudioPlayer implements MediaPlayer {
private MediaAdapter mediaAdapter;
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("mp3")) {
System.out.println("Playing MP3 file: " + fileName);
} else if (audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")) {
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
} else {
System.out.println("Unsupported audio type: " + audioType);
}
}
}
6. 客户端代码
java
public class MusicPlayerApp {
public static void main(String[] args) {
MediaPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3", "song.mp3"); // 输出: Playing MP3 file: song.mp3
audioPlayer.play("mp4", "movie.mp4"); // 输出: Playing MP4 file: movie.mp4
audioPlayer.play("vlc", "video.vlc"); // 输出: Playing VLC file: video.vlc
audioPlayer.play("avi", "video.avi"); // 输出: Unsupported audio type: avi
}
}
2. 外观模式:简化复杂系统的调用方式
故事背景
小明开发了一个家庭影院系统,系统中包含多个设备,比如 DVD 播放器 、投影仪 、音响等。每次看电影时,小明需要依次打开这些设备,操作非常繁琐。
问题出现
小明希望简化操作,只需调用一个方法就能完成所有设备的准备工作。
解决方案:外观模式
小明决定使用外观模式,将多个设备的操作封装成一个统一的接口,从而简化调用方式。
代码实现
1. 定义子系统类
java
// DVD 播放器
class DvdPlayer {
public void on() {
System.out.println("DVD Player is on");
}
public void play(String movie) {
System.out.println("Playing movie: " + movie);
}
public void off() {
System.out.println("DVD Player is off");
}
}
// 投影仪
class Projector {
public void on() {
System.out.println("Projector is on");
}
public void wideScreenMode() {
System.out.println("Projector is in widescreen mode");
}
public void off() {
System.out.println("Projector is off");
}
}
// 音响
class SoundSystem {
public void on() {
System.out.println("Sound System is on");
}
public void setVolume(int level) {
System.out.println("Sound System volume set to " + level);
}
public void off() {
System.out.println("Sound System is off");
}
}
2. 实现外观类
java
// 外观类:HomeTheaterFacade
class HomeTheaterFacade {
private DvdPlayer dvdPlayer;
private Projector projector;
private SoundSystem soundSystem;
public HomeTheaterFacade(DvdPlayer dvdPlayer, Projector projector, SoundSystem soundSystem) {
this.dvdPlayer = dvdPlayer;
this.projector = projector;
this.soundSystem = soundSystem;
}
public void watchMovie(String movie) {
System.out.println("Get ready to watch a movie...");
dvdPlayer.on();
projector.on();
projector.wideScreenMode();
soundSystem.on();
soundSystem.setVolume(10);
dvdPlayer.play(movie);
}
public void endMovie() {
System.out.println("Shutting down the home theater...");
dvdPlayer.off();
projector.off();
soundSystem.off();
}
}
3. 客户端代码
java
public class HomeTheaterApp {
public static void main(String[] args) {
DvdPlayer dvdPlayer = new DvdPlayer();
Projector projector = new Projector();
SoundSystem soundSystem = new SoundSystem();
HomeTheaterFacade homeTheater = new HomeTheaterFacade(dvdPlayer, projector, soundSystem);
homeTheater.watchMovie("Inception"); // 输出: Get ready to watch a movie...
homeTheater.endMovie(); // 输出: Shutting down the home theater...
}
}
3. 总结
- 适配器模式:用于解决接口不兼容的问题,通过适配器将不兼容的接口转换为目标接口。
- 外观模式:用于简化复杂系统的调用方式,通过外观类封装多个子系统的操作。
这两种模式都能帮助我们更好地设计系统,提高代码的可维护性和扩展性。希望本文的讲解和代码示例能帮助你更好地理解适配器模式和外观模式!
互动话题
你在项目中用过适配器模式或外观模式吗?遇到过哪些问题?欢迎在评论区分享你的经验!