秒懂设计模式--学习笔记(10)【结构型-适配器模式】

9、适配器模式

9.1 适配器模式
  • 适配器模式(Adapter)通常也被称为转换器适应与匹配
  • 当一个对象或类的接口不能匹配用户所期待的接口时,适配器就充当中间转换的角色,达到兼容用户接口的目的
  • 适配器也实现了客户端与接口的解耦,提高了组件的可复用性
  • 测试类结构
    适配器模式测试类结构
9.2 翻译(举例)
  • 不懂英文的人与不懂中文的人交流,双方都听不懂对方说什么
  • 找个会两种语言的翻译官
  • 双方通过翻译官进行交流
  • 这个翻译官就相当于适配两个不兼容接口的适配器
9.3 插头与插座(举例)
  • 两脚插头与三孔插座
  • 定义通电标准接口:DualPin(两插)、TriplePin(三相)
package adapter;

/**
 * @Description 两插
 */
public interface DualPin {
    /**
     * 两脚插头: 通电标准
     * @param l 火线(live)
     * @param n 零线(null)
     */
    public void electrify(int l, int n);
}
package adapter;

/**
 * @Description 三相
 */
public interface TriplePin {
    /**
     * 三孔插座: 通电标准
     * @param l 火线(live)
     * @param n 零线(null)
     * @param e 地线(earth)
     */
    public void electrify(int l,int n, int e);
}
  • 定义两插的电视机类TVDualPinImpl
package adapter.impl;
import adapter.DualPin;
/**
 * @Description 两脚插头的电视机类
 */
public class TVDualPinImpl implements DualPin {
    @Override
    public void electrify(int l, int n) {
        System.out.println("火线通电:" + l + ",零线通电:" + n);
        System.out.println("电视开机");
    }
}
  • 但是到这里,可以看到两插和三相的通电便准并不一致,这个两插电视机并不能使用三孔插座进行开机,接口不兼容会提示类型不匹配,如测试类Client.test1()
package adapter;
import adapter.impl.TVDualPinImpl;
/**
 * @Description 测试类
 */
public class Client {
    /**
     * 这个两脚电视机并不能使用三孔插座进行开机
     * 接口不兼容会提示类型不匹配
     */
    private static void test1() {
    	// 这一行会提示错误
        TriplePin triplePinDevice = new TVDualPinImpl();
    }
}
9.4 通用适配器(对象适配器)
  • 极端解决方案: 将三孔插座改成两孔插座,三相变两相。这样的话就不能再使用三相了
  • 如果通过一个中间件来进行兼容适配的话,可以不改变插座原来的功能,而又兼容了两脚插座的使用
  • 制作一个电源转换器AdapterTriplePinImpl来适配,承上启下,解决接口冲突问题
    • 适配器实现三相接口
    • 然后在构造时传入需要被适配的两插接口(表示将两脚接口适配到三孔插座)
    • 实现通电方法时,适配器转换两插,达到适配效果
    • 至此,该适配器就可以将任意两脚插头,匹配到三孔插座了
package adapter.impl;
import adapter.DualPin;
import adapter.TriplePin;
/**
 * @Description 三相适配器(实现三相接口):在三孔插座与两脚插头之间做适配
 */
public class AdapterTriplePinImpl implements TriplePin {
    /**
     * 适配:两插(任何此规格的设备都是可以接入进来的)
     */
    private DualPin dualPin;

    /**
     * 创建适配器:传入需要与三孔插座适配的两脚插头
     * @param dualPin
     */
    public AdapterTriplePinImpl(DualPin dualPin) {
        this.dualPin = dualPin;
    }

    /**
     * 适配器实现
     * @param l 火线(live)
     * @param n 零线(null)
     * @param e 地线(earth)
     */
    @Override
    public void electrify(int l, int n, int e) {
        // 被适配的方法,忽略地线e
        dualPin.electrify(l, n);
    }
}
  • 使用适配器进行接入,Client.test2()
    /**
     * 对象适配器兼容两插和三孔设备
     *      输出结果:
     *          火线通电:1,零线通电:0
     *          电视开机
     */
    private static void test2() {
        // 两插电视机(需要进行适配的)
        DualPin tvDualPin = new TVDualPinImpl();
        // 适配器,串联两插和三孔:将电视机两相插头插入适配器,并将匹配好的适配器插入墙上的三相插孔
        TriplePin triplePin = new AdapterTriplePinImpl(tvDualPin);
        // 底层使用三孔(适配)的通电标准:直接调用三插通电方法给电视机供电
        triplePin.electrify(1, 0, -1);
    }
  • 适配器并不关心接入的设备是电视机还是电冰箱,只要是两相插头的设备均可以进行适配,所以说它是一种通用的适配器
9.5 特定适配器(类适配器)
  • 属于某个类的“专属适配器”,也就是在编码阶段已经将被匹配的设备与目标接口进行对接了
  • 电视机专属适配器类TVAdapter,继承两插电视机类,实现三相接口类:
    • 类适配器模式实现起来更简单
    • 继承两插电视,表明该适配器兼容两插
    • 实现三项接口,表明该适配器适配三相
    • 重写三相通电方法
    • 在通电方法中,使用“super”关键字调用父类(电视机类TV)定义的两插通电方法,以实现适配
package adapter.impl;
import adapter.TriplePin;
/**
 * @Description 电视机专属适配器类:
 *                  类适配器模式实现起来更简单
 *                  继承两插电视,表明该适配器兼容两插
 *                  实现三项接口,表明该适配器适配三相
 */
public class TVAdapter extends TVDualPinImpl implements TriplePin {
    /**
     * 重写三相通电方法
     * @param l 火线(live)
     * @param n 零线(null)
     * @param e 地线(earth)
     */
    @Override
    public void electrify(int l, int n, int e) {
        // “super”关键字调用父类(电视机类TV)定义的两插通电方法,以实现适配
        super.electrify(l, n);
    }
}
  • 测试Client.test3()
    /**
     * 类适配器,类适配器模式不但使用起来更加简单,其效果与对象适配器模式毫无二致
     *      输出结果:
     *          火线通电:1,零线通电:0
     *          电视开机
     */
    private static void test3() {
        // 电视机专属三插适配器插入三相插孔
        TriplePin tvAdapter = new TVAdapter();
        // //此处调用的是三插通电标准
        tvAdapter.electrify(1, 0, -1);
    }
  • 类适配器模式不但使用起来更加简单,而且其效果与对象适配器模式毫无二致。
  • 这个类适配器是继承自电视机的子类,在类定义的时候就已经与电视机完成了接驳
  • 也就是说,类适配器与电视机的继承关系让它固化为一种专属适配器,这就造成了继承耦合
  • 倘若我们需要适配其他两插设备,就不得不再写一个“洗衣机专属适配器”
  • 适配器兼容性较差
9.6 化解难以调和的矛盾
  • 适配器模式让兼容性问题在不必修改任何代码的情况下得以解决,其中适配器类是核心
  • 对象适配器模式的各角色定义如下
    • Target(目标接口):客户端要使用的目标接口标准,TriplePin。
    • Adapter(适配器):实现了目标接口,负责适配(转换)被适配者的接口为目标接口,AdapterTriplePinImpl。
    • Adaptee(被适配者):被适配者的接口标准,目前不能兼容目标接口的问题接口,可以有多种实现类,DualPin。
    • Client(客户端):目标接口的使用者
  • 类适配器模式的各角色定义如下
    • Target(目标接口):客户端要使用的目标接口标准,TriplePin。
    • Adapter(适配器):继承自被适配者类且实现了目标接口,负责适配(转换)被适配者的接口为目标接口,TVAdapter
    • Adaptee(被适配者):被适配者的类实现,目前不能兼容目标接口的问题类,TVDualPinImpl
    • Client(客户端):目标接口的使用者
  • 对象适配器模式与类适配器模式基本相同,二者的区别在于
    • 对象适配器的Adaptee(被适配者)以接口形式出现并被Adapter(适配器)引用(更灵活)
    • 类适配器以父类的角色出现并被Adapter(适配器)继承(更简单)、
    • 适配器至少都应该具备模块两侧的接口特性

相关推荐

  1. 设计模式_结构模式_适配器模式

    2024-07-20 10:04:04       41 阅读
  2. 设计模式结构模式适配器模式

    2024-07-20 10:04:04       47 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-20 10:04:04       51 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-20 10:04:04       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-20 10:04:04       44 阅读
  4. Python语言-面向对象

    2024-07-20 10:04:04       55 阅读

热门阅读

  1. Emacs的插件生态系统

    2024-07-20 10:04:04       18 阅读
  2. ES6 正则的扩展(十九)

    2024-07-20 10:04:04       19 阅读
  3. golang中实现LRU-K算法(附带单元测试)

    2024-07-20 10:04:04       19 阅读
  4. 23年阿里淘天笔试题 | 卡码网模拟

    2024-07-20 10:04:04       15 阅读
  5. 前端经验:使用sheetjs导出CSV文本为excel

    2024-07-20 10:04:04       16 阅读
  6. autohotkey自动化执行vim命令

    2024-07-20 10:04:04       18 阅读
  7. 开源虚拟加密盘VeraCrypt命令行使用方法

    2024-07-20 10:04:04       12 阅读