适配器模式是什么?
适配器模式(Adapter Pattern)是一种结构型的设计模式,它的原理是将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以相互合作。
应用场景
假设有一个运行多年的软件系统,你希望它能和一个新的厂商类库搭配使用,但是这个新厂商所设计出来的接口不同于旧厂商的接口。
你希望在不改动现有代码的前提下,就能解决这个问题(而且你也不能改变厂商的代码)。所以该怎么做?你可以写一个类,将新厂商接口转接成你所期望的接口。
这个适配器工作起来就如同一个中间人,它将客户所发出的请求转换成厂商类能理解的请求。
工作原理
下图描述了适配器模式的工作原理。
根据上图,总结客户端使用适配器的过程:
- 客户端通过目标接口调用适配器的方法,对适配器发出请求。
- 适配器使用被适配者接口把请求转换成被适配者的一个或多个调用。
- 客户端接收到调用的结果,但并未察觉这一切是适配器在起转换作用。
实现方式
适配器的实现方式有两种,类适配器与对象适配器。下面将分别介绍这两种实现方式。
类适配器
类适配器采用继承的方式:适配器通过继承被适配者,与之产生联系。用 UML 类图来描述使用类适配器时模式的结构:
对象适配器
对象适配器则是采用组合的方式:适配器通过关联被适配者,与之产生联系。用 UML 类图来描述使用对象适配器时模式的结构:
二者的比较
采用组合的方式与继承的方式相比,适配器与被适配者的耦合更弱,在实践过程中推荐采用对象适配器。
案例
最后,我们通过一个案例来进一步加强对适配器模式的理解。假设有如下情景:有一栋建造多年的房子,房子里的插座都是三孔的。我们用一个 TriplexOutlet 类来表示三孔插座:
public class TriplexOutlet {
public void useThreeHoles() {
System.out.println("You are using the triplex outlet now.");
}
}
最近,有人送给房子的主人一个新的家用电器,但这个电器的插头是双孔的,它只能与双孔插座搭配使用。我们用一个 DuplexOutlet 接口来表示双孔插座:
public interface DuplexOutlet {
public void useTwoHoles();
}
用一个 DuplexPlug 类来表示双孔插头:
public class DuplexPlug {
public void insert(DuplexOutlet duplexOutlet) {
System.out.println("This is a duplex plug, it could inserts into a duplex outlet only.");
duplexOutlet.useTwoHoles();
}
}
我们先缕一下这几个对象的角色:双孔插头是客户端,它要调用的目标接口是双孔插座,但现在的插座是三孔的,它无法直接被双孔插头使用。我们还需要一个适配器,将三孔插座转换成双孔的接口,这样双孔插头就能够使用这个插座了。
适配器要如何实现呢?我们先看看类适配器的实现方式:
public class OutletAdapter extends TriplexOutlet implements DuplexOutlet {
@Override
public void useTwoHoles() {
System.out.println("This is an outlet adapter, it helps duplex plug adapt to triplex outlet.");
this.useThreeHoles();
}
}
我们通过一个用例来模拟一下类适配器的适配过程:
public class ClassAdapterMain {
public static void main(String[] args) {
DuplexOutlet duplexOutlet = new OutletAdapter();
DuplexPlug duplexPlug = new DuplexPlug();
duplexPlug.insert(duplexOutlet);
}
}
我们再看对象适配器的实现方式:
public class OutletAdapter implements DuplexOutlet {
private TriplexOutlet triplexOutlet;
public OutletAdapter(TriplexOutlet triplexOutlet) {
this.triplexOutlet = triplexOutlet;
}
@Override
public void useTwoHoles() {
System.out.println("This is an outlet adapter, it helps duplex plug adapt to triplex outlet.");
triplexOutlet.useThreeHoles();
}
}
我们通过一个用例来模拟一下类适配器的适配过程:
public class ObjectAdapterMain {
public static void main(String[] args) {
TriplexOutlet triplexOutlet = new TriplexOutlet();
DuplexOutlet duplexOutlet = new OutletAdapter(triplexOutlet);
DuplexPlug duplexPlug = new DuplexPlug();
duplexPlug.insert(duplexOutlet);
}
}
案例源码
可在 GitHub 上查看上述案例的完整代码。
参考资料
本文参考的资料如下:
- Head First Design Patterns - Elisabeth Freeman, Chapter 7.
- https://refactoring.guru/design-patterns/adapter
- https://refactoringguru.cn/design-patterns/adapter
- https://sourcemaking.com/design_patterns/adapter