适配器模式是什么?

适配器模式(Adapter Pattern)是一种结构型的设计模式,它的原理是将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以相互合作。

应用场景

假设有一个运行多年的软件系统,你希望它能和一个新的厂商类库搭配使用,但是这个新厂商所设计出来的接口不同于旧厂商的接口。
image.png
你希望在不改动现有代码的前提下,就能解决这个问题(而且你也不能改变厂商的代码)。所以该怎么做?你可以写一个类,将新厂商接口转接成你所期望的接口。
image.png
这个适配器工作起来就如同一个中间人,它将客户所发出的请求转换成厂商类能理解的请求。
image.png

工作原理

下图描述了适配器模式的工作原理。
image.png
根据上图,总结客户端使用适配器的过程:

  1. 客户端通过目标接口调用适配器的方法,对适配器发出请求。
  2. 适配器使用被适配者接口把请求转换成被适配者的一个或多个调用。
  3. 客户端接收到调用的结果,但并未察觉这一切是适配器在起转换作用。

实现方式

适配器的实现方式有两种,类适配器与对象适配器。下面将分别介绍这两种实现方式。

类适配器

类适配器采用继承的方式:适配器通过继承被适配者,与之产生联系。用 UML 类图来描述使用类适配器时模式的结构:
image.png

对象适配器

对象适配器则是采用组合的方式:适配器通过关联被适配者,与之产生联系。用 UML 类图来描述使用对象适配器时模式的结构:
image.png

二者的比较

采用组合的方式与继承的方式相比,适配器与被适配者的耦合更弱,在实践过程中推荐采用对象适配器。

案例

最后,我们通过一个案例来进一步加强对适配器模式的理解。假设有如下情景:有一栋建造多年的房子,房子里的插座都是三孔的。我们用一个 TriplexOutlet 类来表示三孔插座:

  1. public class TriplexOutlet {
  2. public void useThreeHoles() {
  3. System.out.println("You are using the triplex outlet now.");
  4. }
  5. }

最近,有人送给房子的主人一个新的家用电器,但这个电器的插头是双孔的,它只能与双孔插座搭配使用。我们用一个 DuplexOutlet 接口来表示双孔插座:

  1. public interface DuplexOutlet {
  2. public void useTwoHoles();
  3. }

用一个 DuplexPlug 类来表示双孔插头:

  1. public class DuplexPlug {
  2. public void insert(DuplexOutlet duplexOutlet) {
  3. System.out.println("This is a duplex plug, it could inserts into a duplex outlet only.");
  4. duplexOutlet.useTwoHoles();
  5. }
  6. }

我们先缕一下这几个对象的角色:双孔插头是客户端,它要调用的目标接口是双孔插座,但现在的插座是三孔的,它无法直接被双孔插头使用。我们还需要一个适配器,将三孔插座转换成双孔的接口,这样双孔插头就能够使用这个插座了。
适配器要如何实现呢?我们先看看类适配器的实现方式:

  1. public class OutletAdapter extends TriplexOutlet implements DuplexOutlet {
  2. @Override
  3. public void useTwoHoles() {
  4. System.out.println("This is an outlet adapter, it helps duplex plug adapt to triplex outlet.");
  5. this.useThreeHoles();
  6. }
  7. }

我们通过一个用例来模拟一下类适配器的适配过程:

  1. public class ClassAdapterMain {
  2. public static void main(String[] args) {
  3. DuplexOutlet duplexOutlet = new OutletAdapter();
  4. DuplexPlug duplexPlug = new DuplexPlug();
  5. duplexPlug.insert(duplexOutlet);
  6. }
  7. }

我们再看对象适配器的实现方式:

  1. public class OutletAdapter implements DuplexOutlet {
  2. private TriplexOutlet triplexOutlet;
  3. public OutletAdapter(TriplexOutlet triplexOutlet) {
  4. this.triplexOutlet = triplexOutlet;
  5. }
  6. @Override
  7. public void useTwoHoles() {
  8. System.out.println("This is an outlet adapter, it helps duplex plug adapt to triplex outlet.");
  9. triplexOutlet.useThreeHoles();
  10. }
  11. }

我们通过一个用例来模拟一下类适配器的适配过程:

  1. public class ObjectAdapterMain {
  2. public static void main(String[] args) {
  3. TriplexOutlet triplexOutlet = new TriplexOutlet();
  4. DuplexOutlet duplexOutlet = new OutletAdapter(triplexOutlet);
  5. DuplexPlug duplexPlug = new DuplexPlug();
  6. duplexPlug.insert(duplexOutlet);
  7. }
  8. }

案例源码

可在 GitHub 上查看上述案例的完整代码。

参考资料

本文参考的资料如下: