适配器模式(Adapter)的定义如下:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。适配器模式分为类结构型模式和对象结构型模式两种,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。
由于类适配器模式通过多重继承对一个接口与另一个接口进行匹配,而C#、VB.NET、Java等语言不支持多重继承(C++支持),也就是一个类只有一个父类,所以这里主要将的是对象适配器。其UML图如下:
下面以高级音频播放器能播放MP4和vr文件,将其适配为我们系统的播放器接口,并且保留其播放MP4
和vr文件的功能为例子。
所涉及到的角色:
- 目标接口(这个接口是我们系统需要的)
- 适配者类(这个类是已经存在的,且不好或者无法修改的)
- 适配器类(转接器,通过它可以将适配者转换为我们需要的目标接口类型)
目标接口:
/**
* 播放器
* @author Linging
* @version 1.0.0
* @since 1.0
*/
public interface Player {
/**
* 播放
* @param type 格式
* @param name 播放的歌曲名称
*/
void play(String type, String name);
}
适配者类:
/**
* 高级播放接口,它有两个实现MP4和vr
* @author Linging
* @version 1.0.0
* @since 1.0
*/
public interface AdvancedPlayer {
/**
* 播放MP4
* @param name
*/
void playMP4(String name);
/**
* 播放VR
* @param name
*/
void playVR(String name);
}
/**
* MP4播放器
* @author Linging
* @version 1.0.0
* @since 1.0
*/
public class Mp4Player implements AdvancedPlayer{
@Override
public void playMP4(String name) {
System.out.println("play mp4 file: " + name);
}
@Override
public void playVR(String name) {
}
}
/**
* Vr播放器
* @author Linging
* @version 1.0.0
* @since 1.0
*/
public class VrPlayer implements AdvancedPlayer{
@Override
public void playMP4(String name) {
}
@Override
public void playVR(String name) {
System.out.println("play vr file: " + name);
}
}
适配器类:
/**
* 音频适配器 使最原始的player能适配MP4和vr格式的音频
* @author Linging
* @version 1.0.0
* @since 1.0
*/
public class AudioAdapter implements Player{
AdvancedPlayer advancedPlayer;
public AudioAdapter(String type) {
if(type.equalsIgnoreCase("mp4")){
advancedPlayer = new Mp4Player();
}else if(type.equalsIgnoreCase("vr")){
advancedPlayer = new VrPlayer();
}
}
@Override
public void play(String type, String name) {
if(type.equalsIgnoreCase("mp4")){
advancedPlayer.playMP4(name);
}else if(type.equalsIgnoreCase("vr")){
advancedPlayer.playVR(name);
}
}
}
目标接口的实现:
/**
* 目前有一个音频播放器,内置播放MP3格式的音频
* 但是现在想要播放其他格式的音频,MP4和VR视频,所以需要进行适配
* @author Linging
* @version 1.0.0
* @since 1.0
*/
public class AudioPlayer implements Player{
AudioAdapter adapter;
@Override
public void play(String type, String name) {
if(type.equalsIgnoreCase("mp3")){
System.out.println("play mp3 file: " + name);
}
//适配的格式
else if(type.equalsIgnoreCase("mp4") || type.equalsIgnoreCase("vr")){
adapter = new AudioAdapter(type);
adapter.play(type, name);
}else {
System.out.println("暂不支持播放:" + type + " 类型的文件");
}
}
}
测试:
public class Main {
public static void main(String[] args) {
// AudioPlayer内置默认播放MP3
// 适配之后可以播放mp4和vr
// 适配:AdvancedPlayer接口 --> Player接口
AudioPlayer player = new AudioPlayer();
player.play("mp3", "再给我两分钟");
player.play("mp4", "hahah.mp4");
player.play("vr", "嘿嘿.vr");
}
}
适配器模式总结:
优点:
- 客户端通过适配器可以透明地调用目标接口。
- 复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。
- 将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。
- 在很多业务场景中符合开闭原则。
缺点:
- 适配器编写过程需要结合业务场景全面考虑,可能会增加系统的复杂性。
- 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。