图解设计模式(2)Adapter 模式——加个“适配器”以便于复用


1、Adapter 模式

适配器的角色
在这里插入图片描述

在程序世界中,经常会存在现有的程序无法直接使用,需要做适当的变换之后才能使用的情况。这种用于填补“现有的程序”和“所需的程序”之间差异的设计模式就是 Adapter 模式。

Adapter 模式也被称为 Wrapper 模式。Wrapper 有“包装器”的意思,就像用精美的包装纸将普通商品包装成礼物那样,替我们把某样东西包起来,使其能够用于其他用途的东西就被称为“包装器”或是“适配器”。

Adapter 模式有以下两种。

  • 类适配器模式(使用继承的适配器)
  • 对象适配器模式(使用委托的适配器)

2.2 示例程序(1)(使用继承的适配器)

首先,让我们来看一段使用继承的适配器的示例程序。这里的示例程序是一段会将输入的字符串显示为 (Hello) 或是 * Hello * 的简单程序。

目前在 Banner 类中,有将字符串用括号括起来的 showWithParen 方法,和将字符串用 * 号括起来的 showWithAster 方法。我们假设这个 Banner 类是类似前文中的“交流 100 伏特电压”的“实际情况”。

假设 Print 接口中声明了两种方法,即弱化字符串显示(加括号)的 printWeak(weak 有弱化的意思)方法,和强调字符串显示(加 * 号)的 printStrong(strong 有强化的意思)方法。我们假设这个接口是类似于前文中的“直流 12 伏特电压”的“需求”。

现在要做的事情是使用 Banner 类编写一个实现了 Print 接口的类,也就是说要做一个将“交流 100 伏特电压”转换成“直流 12 伏特电压”的适配器。

扮演适配器角色的是 PrintBanner 类。该类继承了 Banner 类并实现了“需求”——Print 接口。PrintBanner 类使用 showWithParen 方法实现了 printWeak,使用 showWithAster 方法实现了 printStrong。这样,PrintBanner 类就具有适配器的功能了。

电源的比喻和示例程序的对应关系:

电源的比喻示例程序
实际情况交流100伏特Banner 类(showWithParen、showWithAster)
变换装置适配器PrintBanner 类
需求直流12伏特Print 接口(printWeak、printStrong)

使用了“类适配器模式”的示例程序的类图(使用继承):
在这里插入图片描述
代码实现:

Banner 类:

public class Banner {
    private String string;
    public Banner(String string) {
        this.string = string;
    }
    public void showWithParen() {
        System.out.println("(" + string + ")");
    }
    public void showWithAster() {
        System.out.println("*" + string + "*");
    }
}

Print 接口

public interface Print {
    public abstract void printWeak();
    public abstract void printStrong();
}

PrintBanner 类

public class PrintBanner extends Banner implements Print {
    public PrintBanner(String string) {
        super(string);
    }
    public void printWeak() {
        showWithParen();
    }
    public void printStrong() {
        showWithAster();
    }
}

PrintBanner 类扮演适配器的角色。它继承了 Banner 类,继承了 showWithParen 方法和 showWithAster 方法。同时,它又实现了 Print 接口,实现了 printWeak 方法和 printStrong 方法。

Main 类

public class Main {
    public static void main(String[] args) {
        Print p = new PrintBanner("Hello");
        p.printWeak();
        p.printStrong();
    }
}

2.3 示例程序(2)(使用委托的示例程序)

之前的示例程序展示了类适配器模式。下面我们再来看看对象适配器模式。在之前的示例程序中,我们使用“继承”实现适配,而这次我们要使用“委托”来实现适配。

Main 类和 Banner 类与示例程序(1)中的内容完全相同,不过这里我们假设 Print 不是接口而是类(代码清单 2-5)。

也就是说,我们打算利用 Banner 类实现一个类,该类的方法和 Print 类的方法相同。由于在 Java 中无法同时继承两个类(只能是单一继承),因此我们无法将 PrintBanner 类分别定义为 Print 类和 Banner 类的子类。

PrintBanner 类(代码清单 2-6)的 banner 字段中保存了 Banner 类的实例。该实例是在 PrintBanner 类的构造函数中生成的。然后,printWeak 方法和 printStrong 方法会通过 banner 字段调用 Banner 类的 showWithParen 和 showWithAster 方法。

与之前的示例代码中调用了从父类中继承的 showWithParen 方法和 showWithAster 方法不同,这次我们通过字段来调用这两个方法。

这样就形成了一种委托关系(图 2-4)。当 PrintBanner 类的 printWeak 被调用的时候,并不是 PrintBanner 类自己进行处理,而是将处理交给了其他实例(Banner 类的实例)的 showWithParen 方法。

使用了“对象适配器模式”的示例程序的类图(使用委托):
在这里插入图片描述
2-5 Print 类

public abstract class Print {
    public abstract void printWeak();
    public abstract void printStrong();
}

2-6 PrintBanner 类

public class PrintBanner extends Print {
    private Banner banner;
    public PrintBanner(String string) {
        this.banner = new Banner(string);
    }
    public void printWeak() {
        banner.showWithParen();
    }
    public void printStrong() {
        banner.showWithAster();
    }
}


2.4 Adapter 模式中的登场角色

  • Target(对象)

该角色负责定义所需的方法。以本章开头的例子来说,即让笔记本电脑正常工作所需的直流 12 伏特电源。在示例程序中,由 Print 接口(使用继承时)和 Print 类(使用委托时)扮演此角色。

  • Client(请求者)

该角色负责使用 Target 角色所定义的方法进行具体处理。以本章开头的例子来说,即直流 12 伏特电源所驱动的笔记本电脑。在示例程序中,由 Main 类扮演此角色。

  • Adaptee(被适配)

注意不是 Adapt-er(适配)角色,而是 Adapt-ee(被适配)角色。Adaptee 是一个持有既定方法的角色。以本章开头的例子来说,即交流 100 伏特电源。在示例程序中,由 Banner 类扮演此角色。

如果 Adaptee 角色中的方法与 Target 角色的方法相同(也就是说家庭使用的电压就是 12 伏特直流电压),就不需要接下来的 Adapter 角色了。

  • Adapter(适配)

Adapter 模式的主人公。使用 Adaptee 角色的方法来满足 Target 角色的需求,这是 Adapter 模式的目的,也是 Adapter 角色的作用。以本章开头的例子来说,Adapter 角色就是将交流 100 伏特电压转换为直流 12 伏特电压的适配器。在示例程序中,由 PrintBanner 类扮演这个角色。
在类适配器模式中,Adapter 角色通过继承来使用 Adaptee 角色,而在对象适配器模式中,Adapter 角色通过委托来使用 Adaptee 角色。

类适配器模式的类图(使用继承)
在这里插入图片描述
 对象适配器模式的类图(使用委托)
 在这里插入图片描述


2.5 拓展思路的要点

什么时候使用 Adapter 模式?

一定会有读者认为“如果某个方法就是我们所需要的方法,那么直接在程序中使用不就可以了吗?为什么还要考虑使用 Adapter 模式呢?”那么,究竟应当在什么时候使用 Adapter 模式呢?

很多时候,我们并非从零开始编程,经常会用到现有的类。特别是当现有的类已经被充分测试过了,Bug 很少,而且已经被用于其他软件之中时,我们更愿意将这些类作为组件重复利用。

Adapter 模式会对现有的类进行适配,生成新的类。通过该模式可以很方便地创建我们需要的方法群。当出现 Bug 时,由于我们很明确地知道 Bug 不在现有的类(Adaptee 角色)中,所以只需调查扮演 Adapter 角色的类即可。这样一来,代码问题的排查就会变得非常简单。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胡小冰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值