适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
在大规模的系统开发过程中,我们常常碰到诸如以下这些情况:我们需要实现某些功能,这些功能已有还不太成熟的一个或多个外部组件,如果我们自己重新开发这些功能会花费大量时间;所以很多情况下会选择先暂时使用外部组件,以后再考虑随时替换。但这样一来,会带来一个问题,随着对外部组件库的替换,可能需要对引用该外部组件的源代码进行大面积的修改,因此也极可能引入新的问题等等。如何最大限度的降低修改面呢?
Adapter模式就是针对这种类似需求而提出来的。Adapter模式通过定义一个新的接口(对要实现的功能加以抽象),和一个实现该接口的Adapter(适配器)类来透明地调用外部组件。这样替换外部组件时,最多只要修改几个Adapter类就可以了,其他源代码都不会受到影响。
一、什么情况下使用适配器模式
①系统需要使用现有的类,而此类的接口不符合系统的需求。
②想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。这些原类不一定有很复杂的接口。
③(对对象的适配器模式而言)在设计里,需要改变多个已有的子类的接口,如果使用类的适配器模式,就要针对每一个子类做一个适配器类,而这不太实际。
二、适配器模式的结构
适配器模式有类的适配器模式和对象的是配置器模式两种不同的形式。其中类的适配器是通过继承实现的,而对象的适配器是通过对象的委派实现的。
1、类的适配器(继承实现)模式的结构
类的适配器模式把被适配的类的API转换成目标类的API,其静态结构图如下:
从上图可以看出,Adaptee类并没有sampleOperation2()方法,而客户端则期待这个方法。为了使客户端能够使用Adaptee类,提供一个中间环节,即类Adapter,把Adapter的API与Target类的API衔接起来。Adapter与Adaptee是继承关系,这决定了这个适配器模式是类的。
模式所涉及的角色有:
①目标(Target)角色:这就是所期待得到的接口。注意,由于这里讨论的是类适配器模式,因此目标不可以是类。
②源(Adaptee)角色:现有需要适配的接口。
③适配器(Adapter)角色:适配器类是本模式的核心。适配器把源接口换成目标接口。显然,这一角色不可以是接口,而必须是具体类。
实例代码:
//Target的源代码
public interface Target{
//这是源类也有的方法sampleOperation1
void sampleOperation1();
//这是源类没有的方法sampleOperation2
void sampleOperation2();
}
//Adaptee源类的代码
public class Adaptee{
//源类含有的方法sampleOperation1
public void sampleOperation1(){}
}
//适配器类Adapter的代码
public class Adpater extends Adaptee implements Target{
//适配器类补充缺失方法sampleOperation2
public void sampleOperation2(){
//write your code here
}
}
使用一个具体类把源(Adaptee)适配到目标(Target)中,这样一来,如果源以及源的子类都是用此类适配,就行不通了。由于适配器类是源的子类,因此可以在适配器类中置换掉(Override)源的一些方法。
2、对象的适配器模式结构
与类的适配器模式一样,对象的适配器模式把适配的类的API转换成目标类的API,与类的适配器模式不同的是,对象的适配器模式不是使用继承关系连接到Adaptee类,而是使用委派关系连接到Adaptee类。
对象的适配器模式结构如下:
模式的角色有:
①目标(Target)角色:这就是所期待的接口,目标可以是具体的或抽象的类。
②源(Adaptee)角色:现有需要适配的接口。
③适配器(adapter)角色:适配器类是本模式的核心。适配器把源接口转换成目标接口,显然,这一角色必须是具体类。
实例代码:
//Target的源代码
public interface Target{
//这是源类也有的方法sampleOperation1
void sampleOperation1();
//这是源类没有的方法sampleOperation2
void sampleOperation2();
}
//Adaptee源类的代码
public class Adaptee{
//源类含有的方法sampleOperation1
public void sampleOperation1(){}
}
//适配器类Adapter的代码
public class Adpater implements Target{
private Adaptee adaptee;
public Adapter(Adaptee adaptee){
super();
this.adaptee = adaptee;
}
//源类有方法sampleOperation1,可以直接委派
public void sampleOperation1(){
this.adaptee.sampleOperation1();
}
//源类没有方法sampleOperation2,需要适配器补充此方法
public void sampleOperation2(){
//write your code here
}
}
对象适配器的效果:①一个适配器可以把多种不同的源是配到同一个目标。换言之,同一个适配器可以把源类和他的子类都是配到目标接口。②与类的适配器模式相比,要想置换类的方法就不太容易。如果一定要置换掉源类的一个或多个方法,就只好先做一个源类的子类,将源类的方法置换掉,然后在把源类的子类当做真正的源进行适配。③虽然要想置换源类的方法不容易,但是要想增加一些新的方法则方便的很,而且新增加的方法可同时适用于所有的源。