我所理解的设计模式系列·第8篇·IPhone的适配器模式最佳实践之道

2022/02/02 设计模式 共 2637 字,约 8 分钟

相比于装饰器模式而言,适配器模式在实际项目中的运用更加广泛,并且只要有适当的工作经验(三年以下),就一定用到过这种设计模式。举个例子,张三负责的交易中心提供了一个抽象且通用的创建订单接口,该接口原本是给系统内部的其他业务中心提供的,现在万恶的产品经理又提了一个新需求——交易中心的创建订单接口需要暴露给外部系统,将用户在第三方平台下的单导入到本平台中,用于后续对用户进行营促销活动。

这里需要解决的问题就是将外部订单入参包装成交易中心创建订单接口需要的入参,同时需要给一些外部订单不具备的字段赋上一些默认/指定的值,毕竟每一个系统都是有差异的。

而这层包装后提供出去的接口就是经适配器适配后的成果。

design-patttern-8-适配器模式-封面

1. 定义

适配器模式是一种结构型设计模式,它的定义是:“将一个类的接口变换成客户端所需要的另一个接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。”

2. 类图

在适配器模式中,有这样几种角色:

  • 目标角色(Target):当前系统期望能够提供的接口,最终适配器会将现存接口包装成目标角色暴露
  • 适配者角色(Adaptee):期望被适配器包装的接口,是当前系统已经存在的接口
  • 适配器角色(Adapter):将适配者角色装换为目标角色,即所谓的胶水层

适配器模式有两种适配方案,一种是类适配器,一种是对象适配器。

类适配器通过继承适配者角色的方式完成适配,其类图如下:

design-patttern-8-适配器模式-1-类适配器

对象适配器通过组合适配者角色的方式完成适配,其类图如下:

design-patttern-8-适配器模式-2-对象适配器

3. 示例

3.1 IPhone 的适配器模式最佳实践

在《Head First 设计模式》这本书中关于适配器模式的部分,是用电源适配器的例子来讲解的:中国与欧洲的电压标准不同,所以手机的充电器规格是不同的,张三由中国旅欧,到了酒店想要给手机充电,发现从国内带来的充电器插不进欧洲酒店的插座,于是不得已又去买了一个将国内充电器转化为欧洲规格充电器的转换头,这才能够给手机充上电。

design-patttern-8-适配器模式-3-电源适配器

当然上面的例子足以让人理解适配器模式的精髓,但我还是想用 IPhone 手机的例子来再介绍一遍装饰器模式。

记得是在2016年,苹果推出了 IPhone7 系列手机,其中有一项重大变革——取消了3.5mm的耳机孔。砍掉了耳机孔,那用户之前购买苹果手机而留下的采用3.5毫米孔的耳机怎么办?苹果公司当然考虑到了这点,于是在同一时间推出了价值67人民币的闪电转 3.5 毫米耳机插孔转换器。

完美解决历史遗留耳机产品的问题,这就是 IPhone 的适配器模式最佳实践之道。

3.2 Java 语言描述

为了用 Java 语言来描述 IPhone 的适配器模式最佳实践之道,我首先创建了一个耳机接口,它有一个播放音乐的方法,其代码如下:

public interface Headset {
    void playMusic();
}

然后是 IPhone 的两代耳机,均实现了耳机接口,但不同的是,一个使用了圆形插头,一个使用了 TypeC 插头,其代码如下:

public class HeadsetOfRoundPlug implements Headset {

    @Override
    public void playMusic() {
        System.out.println("圆型插头耳机播放音乐");
    }
}

public class HeadsetOfTypeCPlug implements Headset {

    @Override
    public void playMusic() {
        System.out.println("TypeC插头耳机播放音乐");
    }
}

年少轻狂的张三原本就用一台尊贵的 IPhone6S 手机,但是耐不住苹果爸爸出的新款 IPhone 的诱惑,终于将它纳入囊中,但是买来才发现,它竟然没有耳机孔!只能使用 TypeC 插头的耳机听歌!

public class IPhone7 {

    /**
     * 只能使用 TypeC 插头的耳机播放音乐
     */
    public void playMusic(HeadsetOfTypeCPlug headset) {
        headset.playMusic();
    }
}

才气外露的张三丝毫不慌,立马下单价值67人民币的闪电转 3.5 毫米耳机插孔转换器,其代码如下:

public class HeadsetAdapter {

    private HeadsetOfRoundPlug headset;

    public HeadsetAdapter(HeadsetOfRoundPlug headset) {
        this.headset = headset;
    }

    public HeadsetOfTypeCPlug convert() {
        System.out.println("耳机插头适配器将圆形转化为TypeC插头");
        return new HeadsetOfTypeCPlug();
    }
}

然后张三借助耳机适配器将圆形插头耳机转换成 TypeC 插头的耳机,这才成功听上音乐。测试代码如下:

public class Test {
    public static void main(String[] args) {
        // 张三原有的圆形插头耳机
        HeadsetOfRoundPlug oldHeadset = new HeadsetOfRoundPlug();

        // 适配器做转换
        HeadsetAdapter adapter = new HeadsetAdapter(oldHeadset);
        HeadsetOfTypeCPlug convert = adapter.convert();

        // 新买的手机播放音乐
        IPhone7 iPhone7 = new IPhone7();
        iPhone7.playMusic(convert);
    }
}

测试类输出结果如下:

耳机插头适配器将圆形转化为TypeC插头
TypeC插头耳机播放音乐

4. 使用场景

适配器模式的使用场景是:

  • 已经投产的接口由于新需求需要做改造,同时接口定义有变动
  • 使用第三方平台提供的组件,而第三方平台提供的组件接口定义和自己平台的接口定义不同

5. 小结

本文讲述了适配器模式,它的定义是——将一个类的接口变换成客户端所需要的另一个接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。

适配器模式的优点是:

  • 适配器模式可以让两个没有任何关系的类在一起运行,只要适配器角色做好关联
  • 提高透明性,客户端通过适配器能够透明地调用目标角色
  • 提高复用度,相似功能只需要新增适配类

适配器模式的缺点是:

  • 提高代码的复杂度,如果一个接口有多个适配器,且文档不全的情况下,使用者可能不知道调用哪个接口

6. 参考资料

  • 《设计模式之禅》(第2版)

最后,本文收录于个人语雀知识库: 我所理解的后端技术,欢迎来访。

文档信息

Search

    Table of Contents