定义
将某个类的接口转换成使用者期望的另一个接口表示,主要目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。
在适配器模式中,我们通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。
根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。
角色
结构和实现
结构
-
Target(目标抽象类):
目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。 -
Adapter(适配器类):
适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。 -
Adaptee(适配者类):
适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
实现
最典型的例子就是我们的电源适配器,我们都知道我们电源插座的电压是220V,但是我们手机、电脑等电器是不可能用这样的电压直接进行充电的,直接冲会爆的。所以啊,我们不同的电器在购买时都会配备不同的电源适配器来给我们的电器提供合适的电压
电源插座(Adaptee)
public class AC220 {
/**
* 输出220V电压
* @return
*/
public int output220(){
return 220;
}
}
5V接口Target
public interface AC5 {
public int output5();
}
5V适配器
public class AC5Adapter extends AC220 implements AC5 {
@Override
public int output5() {
int v = output220();
v= v-215;
return v;
}
}
测试
public class AdapterClient {
public static void main(String[] args) {
AC220 ac = new AC220();
charge(ac.output220());
AC5Adapter ac2 = new AC5Adapter();
charge(ac2.output5());
}
public static void charge(int v){
if(v>5){
System.out.println("电压太高充不了");
}else{
System.out.println("开始充电");
}
}
}
上面这个例子写的其实不是很好,但是简单来说就是通过一个适配器来实现目标需要的接口来实现适配,同时继承是为了获取被适配类的逻辑。个人建议是用聚合被适配对象的方式实现,可能这样会更直观一些
特点
优点:
- 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。
- 增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了- 适配者的复用性,同一个适配者类可以在多个不同的系统中复用。
- 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。
缺点:
- 对于Java、C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者;
- 适配者类不能为最终类,如在Java中不能为final类,C#中不能为sealed类;
- 在Java、C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。
适用场景:
系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。
想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。