秒懂设计模式--学习笔记(10)【结构型-适配器模式】
目录
- 9、适配器模式
- 9.1 适配器模式
- 9.2 翻译(举例)
- 9.3 插头与插座(举例)
- 9.4 通用适配器(对象适配器)
- 9.5 特定适配器(类适配器)
- 9.6 化解难以调和的矛盾
9、适配器模式
9.1 适配器模式
- 适配器模式(Adapter)通常也被称为转换器(适应与匹配)
- 当一个对象或类的接口不能匹配用户所期待的接口时,适配器就充当中间转换的角色,达到兼容用户接口的目的
- 适配器也实现了客户端与接口的解耦,提高了组件的可复用性
- 测试类结构
9.2 翻译(举例)
- 不懂英文的人与不懂中文的人交流,双方都听不懂对方说什么
- 找个会两种语言的翻译官
- 双方通过翻译官进行交流
- 这个翻译官就相当于适配两个不兼容接口的适配器
9.3 插头与插座(举例)
- 两脚插头与三孔插座
- 定义通电标准接口:DualPin(两插)、TriplePin(三相)
package adapter; /** * @Description 两插 */ public interface DualPin { /** * 两脚插头: 通电标准 * @param l 火线(live) * @param n 零线(null) */ public void electrify(int l, int n); }
package adapter; /** * @Description 三相 */ public interface TriplePin { /** * 三孔插座: 通电标准 * @param l 火线(live) * @param n 零线(null) * @param e 地线(earth) */ public void electrify(int l,int n, int e); }
- 定义两插的电视机类TVDualPinImpl
package adapter.impl; import adapter.DualPin; /** * @Description 两脚插头的电视机类 */ 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; /** * @Description 测试类 */ public class Client { /** * 这个两脚电视机并不能使用三孔插座进行开机 * 接口不兼容会提示类型不匹配 */ private static void test1() { // 这一行会提示错误 TriplePin triplePinDevice = new TVDualPinImpl(); } }
9.4 通用适配器(对象适配器)
- 极端解决方案: 将三孔插座改成两孔插座,三相变两相。这样的话就不能再使用三相了
- 如果通过一个中间件来进行兼容适配的话,可以不改变插座原来的功能,而又兼容了两脚插座的使用
- 制作一个电源转换器AdapterTriplePinImpl来适配,承上启下,解决接口冲突问题
- 适配器实现三相接口
- 然后在构造时传入需要被适配的两插接口(表示将两脚接口适配到三孔插座)
- 在实现通电方法时,适配器转换两插,达到适配效果
- 至此,该适配器就可以将任意两脚插头,匹配到三孔插座了
package adapter.impl; import adapter.DualPin; import adapter.TriplePin; /** * @Description 三相适配器(实现三相接口):在三孔插座与两脚插头之间做适配 */ public class AdapterTriplePinImpl implements TriplePin { /** * 适配:两插(任何此规格的设备都是可以接入进来的) */ private DualPin dualPin; /** * 创建适配器:传入需要与三孔插座适配的两脚插头 * @param dualPin */ public AdapterTriplePinImpl(DualPin dualPin) { this.dualPin = dualPin; } /** * 适配器实现 * @param l 火线(live) * @param n 零线(null) * @param e 地线(earth) */ @Override public void electrify(int l, int n, int e) { // 被适配的方法,忽略地线e dualPin.electrify(l, n); } }
- 使用适配器进行接入,Client.test2()
/** * 对象适配器兼容两插和三孔设备 * 输出结果: * 火线通电:1,零线通电:0 * 电视开机 */ 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; /** * @Description 电视机专属适配器类: * 类适配器模式实现起来更简单 * 继承两插电视,表明该适配器兼容两插 * 实现三项接口,表明该适配器适配三相 */ public class TVAdapter extends TVDualPinImpl implements TriplePin { /** * 重写三相通电方法 * @param l 火线(live) * @param n 零线(null) * @param e 地线(earth) */ @Override public void electrify(int l, int n, int e) { // “super”关键字调用父类(电视机类TV)定义的两插通电方法,以实现适配 super.electrify(l, n); } }
- 测试Client.test3()
/** * 类适配器,类适配器模式不但使用起来更加简单,其效果与对象适配器模式毫无二致 * 输出结果: * 火线通电:1,零线通电:0 * 电视开机 */ 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(适配器)继承(更简单)、
- 适配器至少都应该具备模块两侧的接口特性
- 测试Client.test3()
- 适配器并不关心接入的设备是电视机还是电冰箱,只要是两相插头的设备均可以进行适配,所以说它是一种通用的适配器
- 使用适配器进行接入,Client.test2()
- 但是到这里,可以看到两插和三相的通电便准并不一致,这个两插电视机并不能使用三孔插座进行开机,接口不兼容会提示类型不匹配,如测试类Client.test1()
- 定义两插的电视机类TVDualPinImpl
文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。