适配器模式
这个更加好理解,就是做适配功能的类,例如,现在手机没有了圆形耳机接口,只有Type-C接口,因此你如果还想要使用圆形耳机的话需要买个圆形接口转Type-C的转换器(适配器),这就是所谓的适配器,将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。适配器模式分为类适配器模式和对象适配器模式,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。
适配器模式(Adapter)包含以下主要角色:
- 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
- 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
- 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
类适配器模式
适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件。使用一个例子来说明类适配器模式,现有一台电脑只能读取SD卡,而要读取TF卡中的内容的话就需要使用到适配器。创建一个读卡器,将TF卡中的内容读取出来。
举个非常好理解的例子,就好比A是欧洲人,B是日本人,欧洲有个芯片公司只允许欧洲人外貌的人进入吃饭并学习芯片技术,此时日本人想要进去是不行的,会被门卫驱赶。这个时候B最好的方式就是找个适配器,也就是找一套人皮面具扮演成欧洲人就能进去,进去之后吃饭还是这个日本人吃,学习芯片技术也是这个日本人,只不过使用人皮面具蒙混过关而已。而下面是使用SDAdapterTF
类 通过implements SDCard
来穿上人皮面具,以适配Computer
类方法的参数类型(多态牛逼),而这个多态的运用就是这个人皮面具,而真正功能的实现还是要靠TFCardImpl
来实现,而使用TFCardImpl
又有两种方式,这里讲第一种通过继承的方式——类适配器模式。
类图如下:
代码
首先定义我们的电脑实体类、SD卡实体类和TF卡实体类,电脑类仅能读取SD卡,即参数只能传入SDCard
的子类,如下:
// 定义Computer类
public class Computer {
private String type;
public String readSDCard(SDCard sdCard){
if(sdCard==null){
System.out.println("SD 卡损坏!");
}
return sdCard.readSD();
}
public void writeSDCard(SDCard sdCard,String msg){
sdCard.writeSD(msg);
}
}
// 定义SDCard接口
public interface SDCard {
String readSD();
void writeSD(String msg);
}
// 定义TFCard接口
public interface TFCard {
String readTF();
void writeTF(String msg);
}
// SDCard的实现类
public class SDCardImpl implements SDCard{
@Override
public String readSD() {
return "SD卡读出内容:Hello World!";
}
@Override
public void writeSD(String msg) {
System.out.println("SD卡写入内容:"+msg);
}
}
// TFCard的实现类
public class TFCardImpl implements TFCard{
@Override
public String readTF() {
return "TF卡读出内容:Hello World!";
}
@Override
public void writeTF(String msg) {
System.out.println("TF卡写入内容:"+msg);
}
}
接着定义适配器类,我们电脑只能接受SDCard
的子类,而真正完成TF卡的读取功能的是得是TFCard
的实现类TFCardImpl
,因此这个适配器应该完全具有TFCardImpl
的所有功能,因此需要直接继承TFCardImpl
即可,那么如何还要让适配器成为SDCard
的子类呢?因为前面已经继承了一个类,因此后面我们使用实现方式实现SDCard
接口成为SDCard
的子类。这里废话一句:这里的SDCard
就好比上面举例的欧洲人皮面具,TFCardImpl
好比的是那个日本人。
public class SDAdapterTF extends TFCardImpl implements SDCard{
// 特别注意:继承了实现类,实现了SDCard的接口
@Override
public String readSD() {
return super.readTF(); // 调用继承的父类TFCardImpl的方法
}
@Override
public void writeSD(String msg) {
super.writeTF(msg); // 调用继承的父类TFCardImpl的方法
}
}
客户端测试:
public class Main {
public static void main(String[] args) {
Computer computer = new Computer();
// 对于SD卡是可以直接读取的
SDCardImpl sdCard = new SDCardImpl();
String msg = computer.readSDCard(sdCard);
System.out.println(msg);
computer.writeSDCard(sdCard,"你好,世界!");
// 对于TF卡,不能直接读取,而要借助适配器来调用TF实现类的方法
// TFCardImpl tfCard = new TFCardImpl();
// computer.readSDCard(tfCard)
SDAdapterTF sdAdapterTF = new SDAdapterTF();
msg = computer.readSDCard(sdAdapterTF);
System.out.println(msg);
computer.writeSDCard(sdAdapterTF,"你好世界!");
}
}
输出:
SD卡读出内容:Hello World!
SD卡写入内容:你好,世界!
TF卡读出内容:Hello World!
TF卡写入内容:你好世界!
可以看到,上述适配器SDAdapterTF
实际上就是读卡器嘛!只不过我们的电脑只能接受SD卡的插口,SDAdapterTF
扮演的就是TF转SD接口的读卡器。因此其实是让SDAdapterTF
继承了TFCardImple
,因此可以在里面直接调用TFCardImple
的方法,而SDAdapterTF
由是SDCard
的接口实现类,因此也可以传入到Computer
的被读取,多态是面向对象的灵魂!!!超级灵活!
缺点: 类适配器模式违背了合成复用原则。类适配器是客户类有一个接口规范的情况下可用,反之不可用。那么,这时你可能已经想到了,我可以不继承TFCardImple
吗?直接传入TFCardImpl
不就好了吗,是的,这种模式非常接近我们的日常生活。也就是下面要讲的对象适配器模式。
对象适配器模式
紧接着上面使用的是继承实现,这里我们讲解第二种实现方式,通过构造器方法传递TFCardImpl
对象来实现,实现方式:对象适配器模式可釆用将现有组件库中已经实现的组件引入适配器类中,该类同时实现当前系统的业务接口。我们使用对象适配器模式将读卡器的案例进行改写。类图如下:
代码
这里只是需要修改一下适配器就行,如果不继承TFCardImpl
还想要调用它的方法应该如何做呢?很简单,让TFCardImpl
的对象作为参数传进来就行,如下:
public class SDAdapterTF implements SDCard {
private TFCard tfCard;
public SDAdapterTF(TFCard tfCard){
this.tfCard = tfCard;
}
@Override
public String readSD() {
return tfCard.readTF();
}
@Override
public void writeSD(String msg) {
tfCard.writeTF(msg);
}
}
客户端测试:
public class Main {
public static void main(String[] args) {
Computer computer = new Computer();
// 对于SD卡是可以直接读取的
SDCardImpl sdCard = new SDCardImpl();
String msg = computer.readSDCard(sdCard);
System.out.println(msg);
computer.writeSDCard(sdCard,"你好,世界!");
// 对于TF卡需要是有适配器类读取
TFCard tfCard = new TFCardImpl();
SDAdapterTF sdAdapterTF = new SDAdapterTF(tfCard);
msg = computer.readSDCard(sdAdapterTF);
System.out.println(msg);
computer.writeSDCard(sdAdapterTF,"你好世界!");
}
}
输出:
SD卡读出内容:Hello World!
SD卡写入内容:你好,世界!
TF卡读出内容:Hello World!
TF卡写入内容:你好世界!
对象适配器模式其实更加贴近我们的直觉,一般我们将TF卡使用读卡器插入到电脑,而这里的SDAdapterTF
对象就是 读卡器+TF 卡,只不过类适配器模式将TFCardImpl
直接继承了,相当于焊丝了。而我们的对象适配器模式获取到TFCardImpl
是通过构造方法获取到的,更加灵活!因此总结来说,类适配器直接继承,而对象适配器通过构造方法获取对象,仅此而已!
注意:还有一个适配器模式是接口适配器模式。当不希望实现一个接口中所有的方法时,可以创建一个抽象类Adapter ,实现所有方法。而此时我们只需要继承该抽象类即可。
使用场景: 如果两个类做同一件事(例如本题的存储卡,都是完成数据存取功能的,还比如读取不同编码文件的类)即我有的方法你也要有,只不过各自的方法具体做的不一样,方法中的有些细节不同,可以使用适配器屏蔽掉接口类型的不一致性。
参考内容:
传智播客设计模式相关笔记(主要)
https://zhuanlan.zhihu.com/p/369272002