9、适配器模式
9.1 适配器模式
- 适配器模式(Adapter)通常也被称为转换器(适应与匹配)
- 当一个对象或类的接口不能匹配用户所期待的接口时,适配器就充当中间转换的角色,达到兼容用户接口的目的
- 适配器也实现了客户端与接口的解耦,提高了组件的可复用性
- 测试类结构
![适配器模式测试类结构](https://i-blog.csdnimg.cn/direct/bb55f96be2de4d15993db82ff7752941.png)
9.2 翻译(举例)
- 不懂英文的人与不懂中文的人交流,双方都听不懂对方说什么
- 找个会两种语言的翻译官
- 双方通过翻译官进行交流
- 这个翻译官就相当于适配两个不兼容接口的适配器
9.3 插头与插座(举例)
- 两脚插头与三孔插座
- 定义通电标准接口:DualPin(两插)、TriplePin(三相)
package adapter;
public interface DualPin {
public void electrify(int l, int n);
}
package adapter;
public interface TriplePin {
public void electrify(int l,int n, int e);
}
package adapter.impl;
import adapter.DualPin;
public class TVDualPinImpl implements DualPin {
@Override
public void electrify(int l, int n) {
System.out.println("火线通电:" + l + ",零线通电:" + n);
System.out.println("电视开机");
}
}
- 但是到这里,可以看到两插和三相的通电便准并不一致,这个两插电视机并不能使用三孔插座进行开机,接口不兼容会提示类型不匹配,如测试类Client.test1()
package adapter;
import adapter.impl.TVDualPinImpl;
public class Client {
private static void test1() {
TriplePin triplePinDevice = new TVDualPinImpl();
}
}
9.4 通用适配器(对象适配器)
- 极端解决方案: 将三孔插座改成两孔插座,三相变两相。这样的话就不能再使用三相了
- 如果通过一个中间件来进行兼容适配的话,可以不改变插座原来的功能,而又兼容了两脚插座的使用
- 制作一个电源转换器AdapterTriplePinImpl来适配,承上启下,解决接口冲突问题
- 适配器实现三相接口
- 然后在构造时传入需要被适配的两插接口(表示将两脚接口适配到三孔插座)
- 在实现通电方法时,适配器转换两插,达到适配效果
- 至此,该适配器就可以将任意两脚插头,匹配到三孔插座了
package adapter.impl;
import adapter.DualPin;
import adapter.TriplePin;
public class AdapterTriplePinImpl implements TriplePin {
private DualPin dualPin;
public AdapterTriplePinImpl(DualPin dualPin) {
this.dualPin = dualPin;
}
@Override
public void electrify(int l, int n, int e) {
dualPin.electrify(l, n);
}
}
private static void test2() {
DualPin tvDualPin = new TVDualPinImpl();
TriplePin triplePin = new AdapterTriplePinImpl(tvDualPin);
triplePin.electrify(1, 0, -1);
}
- 适配器并不关心接入的设备是电视机还是电冰箱,只要是两相插头的设备均可以进行适配,所以说它是一种通用的适配器
9.5 特定适配器(类适配器)
- 属于某个类的“专属适配器”,也就是在编码阶段已经将被匹配的设备与目标接口进行对接了
- 电视机专属适配器类TVAdapter,继承两插电视机类,实现三相接口类:
- 类适配器模式实现起来更简单
- 继承两插电视,表明该适配器兼容两插
- 实现三项接口,表明该适配器适配三相
- 重写三相通电方法
- 在通电方法中,使用“super”关键字调用父类(电视机类TV)定义的两插通电方法,以实现适配
package adapter.impl;
import adapter.TriplePin;
public class TVAdapter extends TVDualPinImpl implements TriplePin {
@Override
public void electrify(int l, int n, int e) {
super.electrify(l, n);
}
}
private static void test3() {
TriplePin tvAdapter = new TVAdapter();
tvAdapter.electrify(1, 0, -1);
}
- 类适配器模式不但使用起来更加简单,而且其效果与对象适配器模式毫无二致。
- 这个类适配器是继承自电视机的子类,在类定义的时候就已经与电视机完成了接驳
- 也就是说,类适配器与电视机的继承关系让它固化为一种专属适配器,这就造成了继承耦合
- 倘若我们需要适配其他两插设备,就不得不再写一个“洗衣机专属适配器”
- 适配器兼容性较差
9.6 化解难以调和的矛盾
- 适配器模式让兼容性问题在不必修改任何代码的情况下得以解决,其中适配器类是核心
- 对象适配器模式的各角色定义如下
- Target(目标接口):客户端要使用的目标接口标准,TriplePin。
- Adapter(适配器):实现了目标接口,负责适配(转换)被适配者的接口为目标接口,AdapterTriplePinImpl。
- Adaptee(被适配者):被适配者的接口标准,目前不能兼容目标接口的问题接口,可以有多种实现类,DualPin。
- Client(客户端):目标接口的使用者
- 类适配器模式的各角色定义如下
- Target(目标接口):客户端要使用的目标接口标准,TriplePin。
- Adapter(适配器):继承自被适配者类且实现了目标接口,负责适配(转换)被适配者的接口为目标接口,TVAdapter
- Adaptee(被适配者):被适配者的类实现,目前不能兼容目标接口的问题类,TVDualPinImpl
- Client(客户端):目标接口的使用者
- 对象适配器模式与类适配器模式基本相同,二者的区别在于
- 对象适配器的Adaptee(被适配者)以接口形式出现并被Adapter(适配器)引用(更灵活)
- 类适配器以父类的角色出现并被Adapter(适配器)继承(更简单)、
- 适配器至少都应该具备模块两侧的接口特性