适配器模式(Adapter Pattern)定义如下:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。

适配器模式分为类结构型模式和对象结构型模式两种,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。

在软件设计中也可能出现:需要开发的具有某种业务功能的组件在现有的组件库中已经存在,但它们与当前系统的接口规范不兼容,如果重新开发这些组件成本又很高,这时用适配器模式能很好地解决这些问题。

适配器模式的组成如下:

组成 描述
Target(目标接口) 当前系统业务所期待的接口,它可以是抽象类或接口
Adaptee(适配者) 需要适配的接口
Adapter(适配器) 它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者

类适配器模式结构图如下:

设计模式—适配器模式 - 图1
从上图可以看出:

  1. 问题:Target想调用request方法,但是adaptee并没有这个方法(不兼容)
  2. 解决方法:提供中间件Adapter类,使其实现Target接口,把Adaptee的API和Target的API衔接起来

对象适配器模式结构体如下:

设计模式—适配器模式 - 图2
将适配者Adaptee传入适配器Adapter中,将两者的API衔接调用。

适配器模式的优点

  1. 客户端通过适配器可以透明地调用目标接口
  2. 复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类
  3. 将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题
  4. 在很多业务场景中符合开闭原则

适配器模式的缺点

  1. 适配器编写过程需要结合业务场景全面考虑,可能会增加系统的复杂性
  2. 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱

适配器模式应用场景

  1. 以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致
  2. 使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同


适配器模式使用步骤

  1. 实现目标接口
  2. 内部持有一个待转换接口的引用
  3. 在目标接口的实现方法内部,调用待转换接口的方法


适配器模式使用示例

假设我们已经有一个Task类,实现了Callable接口,现在想使用一个Thread去运行Task,那么就需要将Callable转换为Runnable接口来使用。这就是使用Adapter模式将Callable接口适配为Runnable。

  1. public class AdapterTest {
  2. private static class Task implements Callable<String> {
  3. String params;
  4. public Task(String params) {
  5. this.params = params;
  6. }
  7. @Override
  8. public String call() {
  9. int length = params.length();
  10. if (length > 10) {
  11. return params.substring(5);
  12. }
  13. return params;
  14. }
  15. }
  16. private static class RunnableAdapter implements Runnable {
  17. private Task task;
  18. private RunnableAdapter(Task task) {
  19. this.task = task;
  20. }
  21. @Override
  22. public void run() {
  23. String call = task.call();
  24. System.out.println("result:" + call);
  25. }
  26. }
  27. public static void main(String[] args) {
  28. // 报错:参数类型不对
  29. // Thread thread = new Thread(new Task("bujianjunxi"));
  30. // 通过适配器调用
  31. Task task = new Task("bujianjunxi");
  32. RunnableAdapter adapter = new RunnableAdapter(task);
  33. adapter.run();
  34. }
  35. }

程序运行结果如下:

:::success result:njunxi :::

与外观模式的区别

  • 外观模式的实现核心主要是——由外观类去保存各个子系统的引用,实现由一个统一的外观类去包装多个子系统类,然而客户端只需要引用这个外观类,然后由外观类来调用各个子系统中的方法。
  • 适配器模式是将一个对象包装起来以改变其接口,而外观是将一群对象 ”包装“起来以简化其接口。它们的意图是不一样的,适配器是将接口转换为不同接口,而外观模式是提供一个统一的接口来简化接口