设计模式之适配器模式

发布于:2025-08-01 ⋅ 阅读:(19) ⋅ 点赞:(0)

本文中涉及到的完整代码存放于以下 GitHub 仓库中 LearningCode

1. 理论部分

适配器模式(Adapter Pattern):将一个类的接口转换成客户希望的另一个接口。适配器模式让那些接口不兼容的类可以一起工作。

适配器模式又称包装器模式(Wrapper Pattern)

1.1 结构与实现

适配器模式包含以下 3 个角色:

  • Target(目标接口)
    • 职责:定义客户所需的接口。
    • 实现:Target 一般声明为抽象类或接口,也可以是具体类。
  • Adapter(适配器类)
    • 职责:对 Adaptee 的接口与 Target 接口进行适配。
    • 实现:Adapter 通常声明为具体类。
  • Adaptee(适配者)
    • 职责:定义一个已经存在的接口,这个接口需要适配。
    • 实现:Adaptee 通常声明为具体类。

适配器模式存在以下两种实现方式:

  • 类适配器
  • 对象适配器

1.1.1 类适配器

在类适配器中,Adapter 实现 Target 并继承 Adaptee 类,其 UML 类图如下所示:
在这里插入图片描述
当希望 Adapter 可以重定义 Adaptee 的部分行为时,推荐使用类适配器模式。

1.1.2 对象适配器

在对象适配器中,Adapter 通过继承 Target 并关联一个 Adaptee 对象,其 UML 类图如下所示:
在这里插入图片描述
当希望一个 Adapter 可以匹配 Adaptee及其子类时,推荐使用对象适配器模式。

在不支持多重继承的语言中,如果面临需要继承多个类的情况下,只能使用对象适配器模式。

1.2 扩展:缺省适配器

缺省适配器模式(Default Adapter Pattern):当不需要实现一个接口提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中的每个方法提供一个默认实现,那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中的所有方法的情况,又称为单接口适配器模式

缺省适配器模式包含以下 3 个角色:

  • Target(目标接口):
    • 职责:声明了大量的方法。
    • 实现:Target 通常声明为一个接口。
  • DefaultAdapter(缺省适配器)
    • 职责:实现了 Target 接口,并为接口中的每个方法提供了默认实现。
    • 实现:DefaultAdapter 通常声明为一个抽象类。
  • ConcreteClass(具体业务类):
    • 职责:是 DefaultAdapter 的子类,选择性地重写 DefaultAdapter 中的方法。
    • 实现:ConcreteClass 通常声明为类。

缺省适配器的 UML 类图如下所示:
在这里插入图片描述

1.3 扩展:双向适配器

上面介绍的适配器是单向的,只能实现从 Adaptee 到 Target 的转换,无法反向适配;双向适配器则能够同时兼容两个方向,既可将 Adaptee 适配为 Target,也能将 Target 适配为 Adaptee,从而实现两个接口之间的双向透明通信。在双向适配器中,Adapter 实现了 Target 接口与 Adaptee 接口, 同时包含对 Target 和 Adaptee 的引用,Adaptee 可以通过它调用 Target 中的方法,Target 也可以通过它调用 Adaptee 中的方法。

在不支持多重继承的语言中,如果 Adaptee 与 Target 均为抽象类,则无法实现双向适配器。

双向适配器的 UML 类图如下所示:
在这里插入图片描述

1.4 扩展:参数化适配器

如果要适配多个无继承关系的 Adaptee,势必会创建很多的适配器子类。为了解决这个问题,需要设计一个 Adapter 能够适配多个无继承关系的 Adaptee,即将适配逻辑的实现委托给外部客户端,由客户端负责提供具体的适配行为。其 UML 类图如下所示:

为了便于理解,这里采用 Java 的函数式接口进行描述。
在这里插入图片描述

1.5 优缺点与适用场景

适配器模式具有以下优点:

  • 无论是对象适配器模式还是类适配器模式,都将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。
  • 对于类适配器,可以在适配器类重写适配者的方法,使得适配器的灵活性更强。
  • 对于对象适配器,可以对适配者及其子类进行适配。

适配器模式存在以下缺点:

  • 对于类适配器,在不支持多重类继承的语言中,如果适配者和目标接口都是抽象类,那么类适配器模式将无法实现,有一定的局限性。
  • 对于对象适配器,在适配器中置换适配者的某些方法比较麻烦。

适配器模式适用于以下场景:

  • 需要使用一些已经存在类,而这些类的接口不符合系统的需要,甚至没有这些类的源代码。
  • 想创建一个可以复用的类,用于和一些彼此之间没有太大关联的类一起工作。

2. 实现部分

以 Java 代码为例,演示适配器模式及其扩展的实现。

2.1 适配器模式的实现

案例介绍:
在这里插入图片描述

2.1.1 类适配器

编写目标接口 —— ISensitiveWordsFilter,代码如下所示:

public interface ISensitiveWordsFilter {
    String filter(String text);
}

编写适配者,以 A 系统的过滤系统为例:

public class ASensitiveWordsFilter {
    public String filterObsceneWords(String text) {
        System.out.println("A 敏感词过滤系统:过滤淫秽词语");
        return null;
    }

    public String filterPoliticalWords(String text) {
        System.out.println("A 敏感词过滤系统:过滤涉政词语");
        return null;
    }
}

编写A系统的适配器 —— ASensitiveWordsAdapter:

public class ASensitiveWordsAdapter extends ASensitiveWordsFilter implements ISensitiveWordsFilter {
    @Override
    public String filter(String text) {
        return filterObsceneWords(filterPoliticalWords(text));
    }
}

客户端调用:

public class Main {
    public static void main(String[] args) {
        // 可以使用环境变量或配置文件来优化 ISensitiveWordsFilter 实例的获取
        ISensitiveWordsFilter sensitiveWordsFilter = new ASensitiveWordsAdapter();
        System.out.println(sensitiveWordsFilter.filter(""));
    }
}

完整的 UML 类图如下所示:
在这里插入图片描述

2.1.2 对象适配器

编写A系统的适配器 —— ASensitiveWordsAdapter:

public class ASensitiveWordsAdapter implements ISensitiveWordsFilter {
    private final ASensitiveWordsFilter sensitiveWordsFilter;

    public ASensitiveWordsAdapter(ASensitiveWordsFilter sensitiveWordsFilter) {
        this.sensitiveWordsFilter = sensitiveWordsFilter;
    }

    @Override
    public String filter(String text) {
        return sensitiveWordsFilter.filterObsceneWords(sensitiveWordsFilter.filterPoliticalWords(text));
    }
}

客户端调用:

public class Main {
    public static void main(String[] args) {
        // 可以使用环境变量或配置文件来优化 ISensitiveWordsFilter 实例的获取
        ISensitiveWordsFilter sensitiveWordsFilter = new ASensitiveWordsAdapter(
                new ASensitiveWordsFilter()
        );
        System.out.println(sensitiveWordsFilter.filter(""));
    }
}

完整的UML类图如下所示:
在这里插入图片描述

2.2 扩展:缺省适配器

案例介绍:某公司正在开发一款软件,软件在使用过程会经常播放动画。该软件的开发人员定义了 AnimationListener 用于定义软件在播放动画的过程中会遇到的各种多种动画状态事件,例如 onAnimationStart()(动画开始)、onAnimationEnd()(动画结束)、 onFrameUpdate(float progress)(每一帧更新时,传入播放进度)等。现在需要为不同的动画效果添加监听:

  • 一个淡入淡出的 FadeEffect 只关心 onAnimationStart()(初始化透明度)和 onAnimationEnd()(确保最终透明度)
  • 一个进度条动画 ProgressBar 必须监听 onFrameUpdate()(根据进度更新条的长度)

为了便于开发人员使用,请使用缺省适配器模式进行代码设计。

编写 AnimationListener,代码如下:

public interface AnimationListener {
    void onAnimationStart();

    void onAnimationEnd();

    void onAnimationUpdate();
}

编写 AnimationAdapter,实现AnimationListener中声明的方法,代码如下:

public abstract class AnimationAdapter implements AnimationListener{
    @Override
    public void onAnimationStart() {

    }

    @Override
    public void onAnimationEnd() {

    }

    @Override
    public void onAnimationUpdate() {

    }
}

以淡入淡出动画为例,继承AnimationAdapter,编写动画:

public class FadeEffectAnimation extends AnimationAdapter {
    @Override
    public void onAnimationStart() {
        System.out.println("淡入淡出动画: 开始");
    }

    @Override
    public void onAnimationEnd() {
        System.out.println("淡入淡出动画: 结束");
    }
}

客户端调用:

public class Main {
    public static void main(String[] args) {
        // 播放动画,假设动画总计 10 帧
        AnimationListener animationListener = new FadeEffectAnimation();
        animationListener.onAnimationStart();
        for(int i = 0; i < 10; i++) {
            animationListener.onAnimationUpdate();
        }
        animationListener.onAnimationEnd();
    }
}

完整的 UML 类图如下所示:
在这里插入图片描述

2.3 扩展:双向适配器

暂时没有找到的合适案例,后面遇到了再编写。

如果各位大佬有比较合适的案例,请在评论区留言或者私信我,十分感谢。

2.4 扩展:参数化适配器

案例与2.1相同,参数适配器的目的是将适配逻辑由Adapter内部转交给了客户端,从而减少子类化。
编写 SensitiveWordsAdapter

import java.util.function.Function;

public class SensitiveWordsAdapter<T> implements ISensitiveWordsFilter{
    private final T t;
    private Function<T, Function<String, String>> filterDelegateGenerator;

    public SensitiveWordsAdapter(T t) {
        this.t = t;
    }

    public void setFilterDelegate(Function<T, Function<String, String>> filterDelegateGenerator) {
        this.filterDelegateGenerator = filterDelegateGenerator;
    }

    @Override
    public String filter(String text) {
        return filterDelegateGenerator.apply(t).apply(text);
    }
}

客户端调用:

public class Main {
    public static void main(String[] args) {
        SensitiveWordsAdapter<ASensitiveWordsFilter> sensitiveWordsAdapter = new SensitiveWordsAdapter<ASensitiveWordsFilter>(
                new ASensitiveWordsFilter()
        );
        sensitiveWordsAdapter.setFilterDelegate(aSensitiveWordsFilter -> {
            return text -> {
                return aSensitiveWordsFilter.filterObsceneWords(aSensitiveWordsFilter.filterPoliticalWords(text));
            };
        });
        System.out.println(sensitiveWordsAdapter.filter(""));
    }
}

完整的 UML 类图如下所示:
在这里插入图片描述

3. 参考资料

学习视频:

  1. 设计模式快速入门 —— 图灵星球TuringPlanet —— 适配器模式
  2. Java设计模式详解 —— 黑马程序员 —— 适配器模式(P63 ~ P69)
  3. Java设计模式 —— 尚硅谷 —— 适配器模式(P60 ~ P65)

学习读物:

  1. 《设计模式:可复用面向对象软件的基础》—— Erich Gamma 著 —— 李英军 译 —— 第 4.1 节(P106)
  2. 《Java 设计模式》 —— 刘伟 著 —— 第 9 章(P118)
  3. 《设计模式之美》—— 王争 著 —— 第 6.5 节(P219)
  4. 《设计模式之禅》 —— 第 2 版 —— 秦小波 著 —— 第 19 章(P215)
  5. 《图解设计模式》—— 结城浩 著 —— 杨文轩 译 —— 第 2 章(P14)

电子文献:

  1. 设计模式教程 —— 菜鸟教程 —— 适配器模式
  2. 99+ 种软件模式 —— long2ge —— 适配器模式

网站公告

今日签到

点亮在社区的每一天
去签到