云计算百科
云计算领域专业知识百科平台

设计模式之适配器模式

本文中涉及到的完整代码存放于以下 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. 参考资料

学习视频:

  • 设计模式快速入门 —— 图灵星球TuringPlanet —— 适配器模式
  • Java设计模式详解 —— 黑马程序员 —— 适配器模式(P63 ~ P69)
  • Java设计模式 —— 尚硅谷 —— 适配器模式(P60 ~ P65)
  • 学习读物:

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

  • 设计模式教程 —— 菜鸟教程 —— 适配器模式
  • 99+ 种软件模式 —— long2ge —— 适配器模式
  • 赞(0)
    未经允许不得转载:网硕互联帮助中心 » 设计模式之适配器模式
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!