适配器模式
定义
适配器模式的英文翻译是 Adapter Design Pattern。顾名思义,这个模式就是用来做适配的,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。
一般来说,适配器模式可以看作一种“补偿模式”,用来补救设计上的缺陷。应用这种模式 算是“无奈之举”,如果在设计初期,我们就能协调规避接口不兼容的问题,那这种模式就 没有应用的机会了。
实现方式
类适配器
使用继承关系来实现
IClient
public interface IClient {
void func1();
void func2();
void func3();
void func4();
}
Service
public class Service {
public void funcA() {}
public void funcB() {}
public void func3() {}
public void func4() {}
}
ClassAdaptor
public class ClassAdaptor extends Service implements IClient {
@Override
public void func1() {
super.funcA();
}
@Override
public void func2() {
super.funcB();
}
}
对象适配器
使用组合关系来实现
ObjectAdaptor
public class ObjectAdaptor implements IClient {
private Service service;
public ObjectAdaptor(Service service) {
this.service = service;
}
@Override
public void func1() {
service.funcA();
}
@Override
public void func2() {
service.funcB();
}
@Override
public void func3() {
service.func3();
}
@Override
public void func4() {
service.func4();
}
}
如何选择
Adaptee:服务提供者,需要适配才能使用
ITarget:服务使用者规范
如果Adaptee方法并不多,那两种实现方式都可以。
如果Adaptee接口很多,而且Adaptee和ITarget接口定义大部分都相同,那我们推荐使用 类适配器,因为Adaptor复用父类Adaptee的接口,比起对象适配器的实现方式,Adaptor 的代码量要少一些。
如果Adaptee接口很多,而且Adaptee和ITarget接口定义大部分都不相同,那我们推荐使 用对象适配器,因为组合结构相对于继承更加灵活。
优缺点
- 单一职责原则你可以将接口或数据转换代码从程序主要业务逻辑中分离。
- 开闭原则。 只要客户端代码通过客户端接口与适配器进行交互, 你就能在不修改现有客户端代码的情况下在程序中添加新类型的适配器。
适用场景
封装有缺陷的接口设计
假设我们依赖的外部系统在接口设计方面有缺陷(比如包含大量静态方法),引入之后会影 响到我们自身代码的可测试性。为了隔离设计上的缺陷,我们希望对外部系统提供的接口进 行二次封装,抽象出更好的接口设计,这个时候就可以使用适配器模式了。
统一多个类的接口设计
某个功能的实现依赖多个外部系统(或者说类)。通过适配器模式,将它们的接口适配为统 一的接口定义,然后我们就可以使用多态的特性来复用代码逻辑。
替换依赖的外部系统
当我们把项目中依赖的一个外部系统替换为另一个外部系统的时候,利用适配器模式,可以 减少对代码的改动。
兼容老版本接口
在做版本升级的时候,对于一些要废弃的接口,我们不直接将其删除,而是暂时保留,并且 标注为deprecated,并将内部实现逻辑委托为新的接口实现。这样做的好处是,让使用它的 项目有个过渡期,而不是强制进行代码修改。这也可以粗略地看作适配器模式的一个应用场景。
适配不同格式的数据
前面我们讲到,适配器模式主要用于接口的适配,实际上,它还可以用在不同格式的数据之 间的适配。比如,把从不同征信系统拉取的不同格式的征信数据,统一为相同的格式,以方 便存储和使用。再比如,Java中的Arrays.asList()也可以看作一种数据适配器,将数组类型的 数据转化为集合容器类型。