概述
形象点讲,美国制造的笔记本电源要在中国的插座上充电,首先电源和插座的形状有差别不能直接插上,其次,电源和插座的电压不同,也不能直接使用。为了使电源能够正常的使用,要么更改电源,要么更改插座,要么增加一个适配器(电源和插座都无需改变),显然第三种方案是最佳的。
那么这个适配器该怎么来设计?找出目标接口(从客户入手),然后适配器要实现这个目标接口,在适配器中持有“被适配者”,由客户向适配器发送的request经过转换会交由“被适配者”处理。
软件角度来看,假设已有一个软件系统,原先是使用的旧的厂商的API,现在想要用新厂商提供的接口。但是出现问题如下:
同样,我们不想改变现有系统的代码,也不能改变厂商类的接口,如何解决该问题?可以我们自己或者由厂商提供一个适配器,如下:
对象适配器和类适配器
类适配器:适配器类(adapter)实现目标类(target)继承被适配者类(adaptee),类图如下
对象适配器:被适配者(adaptee)以组合关系被适配器(adapter)持有引用,适配器实现目标接口(target)。类图如下
【扩展】
多用组合,少用继承
何时会使用适配器模式
1.系统需要使用现有的类,而此类的接口不符合系统的需要
注:已有的类的接口不符合系统接口要求。增加一个adapter类,该类实现了系统要求的接口,并持有
已有类的接口类型的引用(委托)。
2.想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,
包括一些可能在将来引进的类一起工作。这些源类不一定有很复杂的接口
注:如xxHelper类(moduleHelper)是可以重复使用的类,
3.在设计里,需要改变已有多个子类的接口,如果使用类的适配器模式,
就要针对每一个子类做一个适配器类。
注:改变接口主要体现为a增加方法 b修改原有方法。
该接口已有多个子类,还要考虑这些类是否已经被系统中其他类广泛引用。
从模式角度来设计的话,为新需求增加一个接口new,同时保留原有的接口old,增加一个adaptor类,
该类实现这两个接口,同时持有old的引用(委托)。
模式举例
示例1:想要遍历一个collection获取其元素,老版本里的做法是集合会返回其元素的枚举(Enumeration),通过该枚举完成遍历,新版本里通常是通过迭代器(Iterator)来遍历。
Enumeration提供方法有:boolean hasMoreElements() 和 E nextElement()。
Iterator提供了:boolean hasNext() 、E next()和 void remove()。
如果对于一个遗留代码,这些代码暴露出枚举器接口,但我们只希望在新的代码中只使用迭代器,可以构造一个适配器,让该适配器实现目标接口(Iterator),同时该适配器组合被适配者(Enumeration),将适配器中的实现转交由被适配者处理(注意remove实现)。
---具体参见《Head First设计模式》
示例2:Runnable/Callable/FutureTask
与其他模式的比较