适配器模式
适配器模式将一个类的接口,转换成客户期望的另一个接口。 适配器模式可以让原本接口不兼容的类可以合作无间。
设计原则 #
- 针对接口编程,而不是针对实现编程
- 多用组合,少用继承
- 为交互对象之间的松耦合而努力
- 类应该对拓展开放,而对修改关闭 (开放-关闭原则)
- 依赖抽象,而不依赖具体类 (依赖倒置原则)
UML简图 #
classDiagram direction LR class Client{ +Adaptor adaptor +otherOperations() } class Target { << Interface >> operationA() } Client *--> Adaptor Adaptor ..|> Target Adaptor *..> Adaptee class Adaptor { +Adaptee adaptee operationA() } class Adaptee { << Interface >> operationB() }
要点 #
- 适配器模式,通过创建"适配器"进行接口转换,可以让不兼容的接口变得兼容。
- 适配器模式让客户从接口的实现解耦。
- 注意适配器和装饰模式的区别,适配器改变接口以让其对不兼容的对象可用, 装饰模式是通过继承,赋予类新的行为和"职责"。
- 当需要使用一个现有的类而可用的接口并不适配时,考虑使用适配器模式。
对象适配器和类适配器
在Java中,看不到类适配器。类适配器,只有在支持多继承(C++)的语言中 适用。类似配器,就是被适配的对象为实例类,同时适配器继承之。
示例代码 #
Generated by
Gemini 1.5-flash
, revised.
普通的播放器只能播放音频,无法播放vlc和mp4等视频,需要借助“高级播放器”的能力。示例代码展示了如何让普通的媒体播放器具备播放视频的能力。
在这种情况下,如果不想修改普通媒体播放器已经存在的代码,就可以使用适配器模式,
Target(适配器的作用对象) #
1// 目标接口(Target)
2// 普通的媒体播放器
3interface MediaPlayer {
4 void play(String audioType, String fileName);
5}
Adaptee(提供能力的对象) #
1// 要适配的接口(Adaptee)
2interface AdvancedMediaPlayer {
3 void playVlc(String fileName);
4 void playMp4(String fileName);
5}
6// 适配者的实现(Adaptee‘s implements)
7class VlcPlayer implements AdvancedMediaPlayer {
8 @Override
9 public void playVlc(String fileName) {
10 System.out.println("正在播放 VLC 文件: " + fileName);
11 }
12 @Override
13 public void playMp4(String fileName) {
14 // do nothing
15 }
16}
17
18class Mp4Player implements AdvancedMediaPlayer {
19 @Override
20 public void playVlc(String fileName) {
21 // do nothing
22 }
23 @Override
24 public void playMp4(String fileName) {
25 System.out.println("正在播放 MP4 文件: " + fileName);
26 }
27}
Adapter(适配器) #
1// 适配器类(Adapter)
2class MediaAdapter implements MediaPlayer {
3 private AdvancedMediaPlayer mediaPlayer;
4
5 public MediaAdapter(String mediaType) {
6 if (mediaType.equalsIgnoreCase("vlc")) {
7 mediaPlayer = new VlcPlayer();
8 } else if (mediaType.equalsIgnoreCase("mp4")) {
9 mediaPlayer = new Mp4Player();
10 }
11 }
12 @Override
13 public void play(String mediaType, String fileName) {
14 if (mediaType.equalsIgnoreCase("vlc")) {
15 mediaPlayer.playVlc(fileName);
16 } else if (mediaType.equalsIgnoreCase("mp4")) {
17 mediaPlayer.playMp4(fileName);
18 }
19 }
20}
客户端 #
1// 客户端类
2class AudioPlayer implements MediaPlayer {
3 private MediaAdapter adapter;
4
5 public void play(String mediaType, String fileName) {
6 if (mediaType.equalsIgnoreCase("mp3")) {
7 System.out.println("正在播放 MP3 文件: " + fileName);
8 } else if (mediaType.equalsIgnoreCase("vlc")
9 || mediaType.equalsIgnoreCase("mp4")) {
10 adapter = new MediaAdapter(mediaType);
11 adapter.play(mediaType, fileName);
12 } else {
13 System.out.println("不支持的文件格式");
14 }
15 }
16}
17
18public class AdapterPatternDemo {
19 public static void main(String[] args) {
20 AudioPlayer audioPlayer = new AudioPlayer();
21 audioPlayer.play("mp3", "beyond the horizon.mp3");
22 audioPlayer.play("vlc", "far far away.vlc");
23 audioPlayer.play("mp4", "alone.mp4");
24 }
25}
话外 #
感觉有点怪,究竟是怎样的场景,需要使用到适配器模式?我想肯定不会是 开发计划期或alpha版本开发期。
出现需要使用适配器的场景,应该是出现在代码迭代期,可能有更新版本(不兼容) 的API需要适配,又为了避免大量修改已经存在的代码。
可能,适配器模式是代码系统优化或者重构的时候,需要考虑的模式?