一天一个设计模式:适配器模式
概念:
适配器模式是把一个类的接口变成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
用途:
就像插头转换器,之前入了switch港版,插头是英式的,还好附赠一个插头转换器,适配器就相当于这个转换器。
种类:
分为类的适配器与对象的适配器两种
类适配器:
把适配的类的api转化为目标类的api
上图中Adaptee没有2方法,但是客户端却期望调用2方法。为了客户端能够使用Adaptee类,提供中间类Adapter,把Adaptee与Target的api组合起来,Adapter与Adaptee是继承关系,所以这决定了适配器是类适配器。
涉及到的角色:
目标(Target)角色:这就是所期待得到的接口。注意:由于这里讨论的是类适配器,因此目标不能是类。
源(Adapee)角色:现在需要适配的接口。
适配器(Adapter)角色:适配器类是本模式的核心。适配器把源接口转化成目标接口,显然这一角色不可以是接口必须是具体类。
类适配器代码:
public interface Target { /** * 这是源类Adaptee也有的方法 */ public void sampleOperation1(); /** * 这是源类Adapteee没有的方法 */ public void sampleOperation2(); }
public class Adaptee { public void sampleOperation1(){} }
public class Adapter extends Adaptee implements Target { /** * 由于源类Adaptee没有方法sampleOperation2() * 因此适配器补充上这个方法 */ @Override public void sampleOperation2() { //写相关的代码 } }
适配器Adapter扩展了源类,也实现了目标接口,且提供了源类没有的接口的实现。
对象适配器模式
对象适配器与类适配器不同的地方在于,对象适配器采用委派的方式将源类与适配器关联到一起(类适配器采用继承)
源代码:
public interface Target { /** * 这是源类Adaptee也有的方法 */ public void sampleOperation1(); /** * 这是源类Adapteee没有的方法 */ public void sampleOperation2(); }
public class Adaptee { public void sampleOperation1(){} }
public class Adapter { private Adaptee adaptee; public Adapter(Adaptee adaptee){ this.adaptee = adaptee; } /** * 源类Adaptee有方法sampleOperation1 * 因此适配器类直接委派即可 */ public void sampleOperation1(){ this.adaptee.sampleOperation1(); } /** * 源类Adaptee没有方法sampleOperation2 * 因此由适配器类需要补充此方法 */ public void sampleOperation2(){ //写相关的代码 } }
类适配器与对象适配器的权衡
- 类适配器使用对象继承的方式,是静态的定义方式;而对象适配器使用的是对象组合的方式,是动态组合的方式。
- 对于类适配器,由于适配器直接继承了Adaptee,使得适配器不能和Adaptee的子类一起工作,因为继承是静态的,当适配器继承Adaptee后,就不能再处理,Adaptee的子类。
- 对于对象适配器,一个适配器可以把不同的源类适配到同一个目标中,换一种说法,同一个适配器可以把源类和它的子类都适配到目标接口。因为对象适配器采用的是对象组合的关系,只要类型正确,是不是子类都无所谓。
- 对于类适配器,适配器可以重定义部分Adaptee的部分行为,相当于子类覆盖父类的方法。
- 对于对象适配器,要重新定义源类的的行为很困难,这种情况下,需要定义Adaptee的子类来实现重定义,然后让适配器重新适配,虽然重定义Adaptee比较困难,但是想要增加一些新的行为则很方便,而且新增的行为可以适配所有的源。
- 对于类适配器,仅仅引入了一个对象,并不需要额外的引用来间接得到Adaptee。
- 对于对象适配器,需要额外的引用来简介得到Adaptee。
综上,尽量以对象适配器的实现方式为主,多用聚合,少用继承。当然具体问题具体分析,根据实际需要选择(个人认为实际上就没必要对源类进行重写,如果需要进行重写直接对源类进行修改就行了,除非是没有代码权限,不过如果没有权限的话,那么源类对你来讲更不可见,也就更没有重写的必要了吧。)。
适配器的优点:
更好的复用性:
系统需要使用现有的类,而此类的接口不符合系统的需要,那么通过适配器模式可以让类获得更好的复用。
更好的扩展性:
在适配器实现功能的时候,也可以调用自己的功能,从而扩展系统的功能。
适配器的缺点:
过多的适配器,会让系统显得更加凌乱,不容易把控。
重构的代价合适的话,确实要比适配器更合适。