适配器模式(Adapter)
适配器模式是一种结构型设计模式(结构型设计模式包含桥接、适配器、组合、装饰器、外观、享元、代理,共有七种),其核心的思想就是在两个独立或者不兼容的接口之间搭建一座桥梁,通过这个桥梁可以将两者组合起来。我们在使用适配器模式是存在前提条件,那就是系统中已经存在了两个原来就独立的功能(接口),为了能够将原有的功能复用起来,而不是修改代码,通过增加一个适配器对象将两个独立功能连接起来,让一个功能接口去适配另外一个功能接口完成新的需求任务,所以适配器的使用也是设计模式7大原则中开闭原则的体现。
但是要非常注意的一点就是如果原来就没有两个独立的功能,个人建议不要使用适配器去实现新的功能需求,因为适配器是一个会增加代码复杂度的设计模式,除非迫不得已否则不要轻易使用。有的文章直接用一句话就概括了适配器模式的使用场景:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。
适配器模式的角色
- 适配器对象(TFCardAdapterSDCard)
适配器设计模式是一个很特殊的设计模式,从上面的类图关系上看,如果去掉了TFCardAdapterSDCard和TFCard,让TFCardImpl去实现SDCard,那么就会变成了上一篇文章说的桥接模式。适配器对象的作用就是将两个独立不兼容的接口SDCard和TFCard组合起来,这样对于依赖SDCard的上层代码(Computer)就不需要做任何修改。
- 独立和不兼容的接口(TFCard和SDCard)
对于定义两个接口是否兼不兼容其实并不难,参数不一致、方法名不一致、返回结果不相同都可以认为是不兼容的接口。实际的开发场景更多的是由于SDCard和TFCard之前已经存在,并且需要兼容两者去实现新功能的时候才会使用适配器模式,否则TFCardImpl可以直接实现SDCard即可。
实践适配器模式
假设现在已经存在笔记本电脑读取SD卡功能的情况下,实现通过不改变上层调用代码的情况下,实现同时可以读取TF卡的新需求。
public interface SDCard {
//读取SD卡方法
String readSD();
//写入SD卡功能
int writeSD(String msg);
}
public class SDCardImpl implements SDCard {
@Override
public String readSD() {
String msg = "sdcard read a msg :hello word SD";
return msg;
}
@Override
public int writeSD(String msg) {
System.out.println("sd card write msg : " + msg);
return 1;
}
}
public interface Computer {
String readSD(SDCard sdCard);
}
public class ThinkpadComputer implements Computer {
@Override
public String readSD(SDCard sdCard) {
if(sdCard == null)throw new NullPointerException("sd card null");
return sdCard.readSD();
}
}
上面的代码已经实现笔记本电脑读取SD卡内容功能,并且是运用了桥接模式实现的。可以参考下面的类关系图:
现在我们希望在不修改上层调用代码的情况下,能够复用下面已经存在了的读取TF卡功能,让笔记本电脑也可以读取TF卡。
public interface TFCard {
String readTF();
int writeTF(String msg);
}
public class TFCardImpl implements TFCard {
@Override
public String readTF() {
String msg ="tf card reade msg : hello word tf card";
return msg;
}
@Override
public int writeTF(String msg) {
System.out.println("tf card write a msg : " + msg);
return 1;
}
}
假设TFCard这个接口和SDCard接口是不兼容的,并且我们想复用已经实现该功能的对象TFCardImpl,那么就需要创建一个适配器对象,让TFCard接口兼容SDCard。
public class SDAdapterTF implements SDCard {
private TFCard tfCard;
public SDAdapterTF(TFCard tfCard) {
this.tfCard = tfCard;
}
@Override
public String readSD() {
System.out.println("adapter read tf card ");
return tfCard.readTF();
}
@Override
public int writeSD(String msg) {
System.out.println("adapter write tf card");
return tfCard.writeTF(msg);
}
}
public class ComputerReadDemo {
public static void main(String[] args) {
Computer computer = new ThinkpadComputer();
SDCard sdCard = new SDCardImpl();
System.out.println(computer.readSD(sdCard));
System.out.println("====================================");
TFCard tfCard = new TFCardImpl();
SDCard tfCardAdapterSD = new SDAdapterTF(tfCard);
System.out.println(computer.readSD(tfCardAdapterSD));
}
}
上传调用代码不需要做任何修改,因为Computer依赖的是SDCard这个接口,并不依赖具体的实例类。
执行结果如下:
sdcard read a msg :hello word SD
====================================
adapter read tf card
tf card reade msg : hello word tf card
使用场景
在软件系统开发中,随着功能的扩展迭代,常常需要让以前的代码能够“适应”新的需求,提高代码的复用性,同时尽量减少对上层调用代码的修改,这时候就需要使用到适配器设计模式,通过添加适配器对象,让一个接口适配另外一个接口,满足功能需求的同时还能复用代码,确实是一个不错的选择。
但是过多的使用适配器会让系统变得非常凌乱,增加代码复杂度,一个系统如果出现太多次使用适配器模式的情况那会是一件非常糟糕的事情,因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。