名称
适配器模式(ADAPTER),别名:Wrappe
目的
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
适用性
以下情况使用Adapter模式
想使用一个已经存在的类,而它的接口不符合你的需求。
想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
想使用一些已经存在的子类(仅适用于对象Adapter),但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
结构
使用多重继承对一个接口与另一个接口进行匹配,适配器为:类适配器。(注:此图与GOF设计模式的结构图不一样,修改了implementation的位置
)
对象匹配器依赖于对象组合,适配器为:对象适配器
目标(Target):定义Client使用的与特定领域相关的接口。
被适配者(Adaptee):定义一个已经存在的接口,这个接口需要适配。
适配器(Adapte):对Adaptee的接口与Target接口进行适配。
客户端(CLient):与符合Target接口的对象协同。
协作
Client在Adapter实例上调用一些操作。接着适配器调用Adaptee的操作实现这个请求。
效果
类适配器
优点
使得Adapter可以重定义Adaptee的部分行为,因为Adapter是Adaptee的一个子类。
仅仅引入了一个对象,并不需要额外的指针以间接得到adaptee。
简化接口。通过适配器可以隐藏复杂的细节,并向客户端提供一个简洁的接口。
单一职责原则。适配器类专注于转换接口,使得职责更加明确。
缺点
用一个具体的Adapter类对Adaptee和Target进行匹配。结果是当我们想要匹配一个类以及所有它的子类时,类Adapter将不能胜任工作。
违反开闭原则。如果需要添加新的适配行为,则需要创建新的适配器子类,这可能违反“对扩展开放,对修改关闭”的原则。
继承层次结构的限制。Java中的单继承(很多语言都是)特性意味着一个适配器类只能适配一个类,不能同时适配多个类。
增加系统的复杂性。过多的适配器类会导致系统结构变得复杂,难以理解和维护。
耦合度较高。适配器类与被适配的类紧密耦合,如果被适配的类发生变化,可能需要修改适配器类。
灵活性较低。使用继承的方式实现适配器,一旦继承关系确定后,很难再改变。
对象适配器
优点
允许一个Adapter与多个Adaptee(即Adaptee本身以及它的所有子类,如果有子类的话)同时工作,这使得适配器更灵活且更易于维护和扩展。Adapter也可以一次给所有的Adaptee添加功能。
符合开闭原则。新增适配需求时可以通过添加新的适配器来实现,而不需要修改现有的适配器或被适配的类。
单一职责原则。适配器类只负责将一个接口转换成另一个接口,职责单一。
缺点
使得重定义Adaptee的行为比较困难。这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。
性能开销。由于对象适配器是通过组合实现的,每次调用适配器的方法时都可能涉及到额外的对象引用查找和间接调用,这可能会带来一定的性能开销。
应用
类适配器
问题
假如我们有一个英式插座并且和平时使用的电器不兼容,这个时候需要一个适配器去进行适配,适配器的作用就是插在英式插座上并且提供一个中式插座,起到桥梁的作用。此时英式插座就是目标,中式插座是被适配者,而适配器就是连接两者的桥梁,继承被适配者类(中式插座)并实现目标(英式插座)接口,适配器通过调用父类方法实现连接被适配类。
示例
UML
代码示例
目标:英式插座接口和实现类
package com.ysj.part4.adapter.class1.target;
public interface BritishStandard {
String getBritishStandard();
}
package com.ysj.part4.adapter.class1.target;
public class BritishStandardImpl implements BritishStandard{
@Override
public String getBritishStandard() {
return "英式插座";
}
}
被适配类:中式插座
package com.ysj.part4.adapter.class1.adaptee;
public class ChineseStandard {
public String getChineseStandard() {
return "中式插座";
}
}
适配器:插座适配器
package com.ysj.part4.adapter.class1.adapter;
import com.ysj.part4.adapter.class1.target.BritishStandard;
import com.ysj.part4.adapter.class1.adaptee.ChineseStandard;
public class StandardAdapter extends ChineseStandard implements BritishStandard {
@Override
public String getBritishStandard() {
return this.getChineseStandard();
}
}
客户端:调用方
package com.ysj.part4.adapter.class1;
import com.ysj.part4.adapter.class1.target.BritishStandard;
import com.ysj.part4.adapter.class1.adapter.StandardAdapter;
/**
* 适配器模式
* 将一个类的接口转换成客户希望的另外一个接口。 Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
* 类适配器:使用多重继承对一个接口与另一个接口进行匹配
*/
public class Client {
public static void main(String[] args) {
/*
* 充电时,对象传入适配器对象,虽然引用是BritishStandard,但是实例对象是StandardAdapter,
* 所以相当于调用StandardAdapter的getBritishStandard,然后调用了父类ChineseStandard的getChineseStandard
*/
charge(new StandardAdapter());
}
/**
* 充电
* @param britishStandard
*/
private static void charge(BritishStandard britishStandard) {
if ("中式插座".equals(britishStandard.getBritishStandard())) {
System.out.println("充电成功!");
} else {
System.out.println("充电失败!");
}
}
}
对象适配器
问题
与类适配器相似,只是在适配器内部从调用父类(被适配类)方法改为了调用被适配类对象方法,原理一样。
示例
UML
代码示例
目标:英式插座接口和实现类
package com.ysj.part4.adapter.object.target;
public interface BritishStandard {
String getBritishStandard();
}
package com.ysj.part4.adapter.object.target;
public class BritishStandardImpl implements BritishStandard {
@Override
public String getBritishStandard() {
return "英式插座";
}
}
被适配类:中式插座
package com.ysj.part4.adapter.object.adaptee;
public class ChineseStandard {
public String getChineseStandard() {
return "中式插座";
}
}
适配器:插座适配器
package com.ysj.part4.adapter.object.adapter;
import com.ysj.part4.adapter.object.target.BritishStandard;
import com.ysj.part4.adapter.object.adaptee.ChineseStandard;
public class StandardAdapter implements BritishStandard {
private ChineseStandard chineseStandard;
public StandardAdapter(ChineseStandard chineseStandard) {
this.chineseStandard = chineseStandard;
}
@Override
public String getBritishStandard() {
return chineseStandard.getChineseStandard();
}
}
客户端:调用方
package com.ysj.part4.adapter.object;
import com.ysj.part4.adapter.object.target.BritishStandard;
import com.ysj.part4.adapter.object.adapter.StandardAdapter;
import com.ysj.part4.adapter.object.adaptee.ChineseStandard;
/**
* 适配器模式
* 将一个类的接口转换成客户希望的另外一个接口。 Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
* 类适配器:使用多重继承对一个接口与另一个接口进行匹配
*/
public class Client {
public static void main(String[] args) {
/*
* 充电时,对象传入适配器对象,适配器对象中传入ChineseStandard对象,虽然引用是BritishStandard,但是实例对象是StandardAdapter,
* 所以相当于调用StandardAdapter的getBritishStandard,在StandardAdapter中通过ChineseStandard对象调用ChineseStandard的getChineseStandard
*/
charge(new StandardAdapter(new ChineseStandard()));
}
/**
* 充电
* @param britishStandard
*/
private static void charge(BritishStandard britishStandard) {
if ("中式插座".equals(britishStandard.getBritishStandard())) {
System.out.println("充电成功!");
} else {
System.out.println("充电失败!");
}
}
}
已知应用
JUnit
JUnit 中的 TestAdapter 用于将旧版本的测试类适配为新版本的 JUnit 测试运行器可以识别的形式。
评论区