适配器模式的探究

1.意图

适配器模式是一种结构型设计模式, 它能使接口不兼容的对象能够相互合作。

2.问题提出

  • 假如你正在开发一款股票市场监测程序, 它会从不同来源下载 XML 格式的股票数据, 然后向用户呈现出美观的图表。
  • 在开发过程中, 你决定在程序中整合一个第三方智能分析函数库。 但是遇到了一个问题, 那就是分析函数库只兼容 JSON 格式的数据。
  • 你可以修改程序库来支持 XML。 但是, 这可能需要修改部分依赖该程序库的现有代码。 甚至还有更糟糕的情况, 你可能根本没有程序库的源代码, 从而无法对其进行修改。

3.解决方案

  • 你可以创建一个适配器。 这是一个特殊的对象, 能够转换对象接口, 使其能与其他对象进行交互。
  • 适配器模式通过封装对象将复杂的转换过程隐藏于幕后。被封装的对象甚至察觉不到适配器的存在。例如, 你可以使用一个将所有数据转换为英制单位 (如英尺和英里) 的适配器封装运行于米和千米单位制中的对象。
  • 适配器不仅可以转换不同格式的数据,其还有助于采用不同接口的对象之间的合作。 它的运作方式如下:
    • 适配器实现与其中一个现有对象兼容的接口。
    • 现有对象可以使用该接口安全地调用适配器方法。
    • 适配器方法被调用后将以另一个对象兼容的格式和顺序将请求传递给该对象。
    • 有时你甚至可以创建一个双向适配器来实现双向转换调用。
  • 为了解决数据格式不兼容的问题, 你可以为分析函数库中的每个类创建将 XML 转换为 JSON 格式的适配器, 然后让客户端仅通过这些适配器来与函数库进行交流。 当某个适配器被调用时, 它会将传入的 XML 数据转换为 JSON 结构, 并将其传递给被封装分析对象的相应方法。

4.适配器模式结构

  • 对象适配器
    实现时使用了构成原则: 适配器实现了其中一个对象的接口, 并对另一个对象进行封装。 所有流行的编程语言都可以实现适配器。
  • 类适配器
    这一实现使用了继承机制: 适配器同时继承两个对象的接口。 请注意, 这种方式仅能在支持多重继承的编程语言中实现, 例如 C++。

5.以“方钉和圆孔” 问题分析适配器模式

适配器假扮成一个圆钉 (RoundPeg), 其半径等于方钉 (SquarePeg) 横截面对角线的一半 (即能够容纳方钉的最小外接圆的半径),下面给出模式结构和伪代码。

//假设你有两个接口相互兼容的类:圆孔(RoundHole)和圆钉(RoundPeg)。
class RoundHole is
        constructor RoundHole(radius) { ... }

        method getRadius() is
        // 返回孔的半径。
        method fits(peg: RoundPeg) is
        return this.getRadius() >= peg.radius()
class RoundPeg is
        constructor RoundPeg(radius) { ... }

        method getRadius() is
// 返回钉子的半径。

// 但还有一个不兼容的类:方钉(SquarePeg)。
class SquarePeg is
        constructor SquarePeg(width) { ... }
        method getWidth() is
// 返回方钉的宽度。

// 适配器类让你能够将方钉放入圆孔中。它会对 RoundPeg 类进行扩展,以接收适
// 配器对象作为圆钉。
class SquarePegAdapter extends RoundPeg is
        // 在实际情况中,适配器中会包含一个 SquarePeg 类的实例。
        private field peg: SquarePeg
        constructor SquarePegAdapter(peg: SquarePeg) is
        this.peg = peg

        method getRadius() is
        // 适配器会假扮为一个圆钉,
        // 其半径刚好能与适配器实际封装的方钉搭配起来。
        return peg.getWidth() * Math.sqrt(2) / 2

// 客户端代码中的某个位置。
        hole = new RoundHole(5)
        rpeg = new RoundPeg(5)
        hole.fits(rpeg) // true

        small_sqpeg = new SquarePeg(5)
        large_sqpeg = new SquarePeg(10)
        hole.fits(small_sqpeg) // 此处无法编译(类型不一致)。

        small_sqpeg_adapter = new SquarePegAdapter(small_sqpeg)
        large_sqpeg_adapter = new SquarePegAdapter(large_sqpeg)
        hole.fits(small_sqpeg_adapter) // true
        hole.fits(large_sqpeg_adapter) // false

6.适用场景

  • 当你希望使用某个类,但是其接口与其他代码不兼容时,可以使用适配器类。
  • 适配器模式允许你创建一个中间层类, 其可作为代码与遗留类、第三方类或提供怪异接口的类之间的转换器。
  • 当你希望复用多个由于缺少相同功能而无法被添加到超类的已有子类时,可以使用该模式。
  • 你可以扩展每个子类,将缺少的功能添加到新的子类中。但是,你必须在所有新子类中重复添加这些代码。
  • 将缺失功能添加到一个适配器类中是一种优雅得多的解决方案。然后你可以将缺少功能的对象封装在适配器中,从而动态地获取所需功能。如要这一点正常运作,目标类必须要有通用接口,适配器的成员变量应当遵循该通用接口。这种方式同装饰模式非常相似。

7.适配器模式优缺点

  • 单一职责原则。你可以将接口或数据转换代码从程序主要业务逻辑中分离。
  • 开闭原则。 只要客户端代码通过客户端接口与适配器进行交互, 你就能在不修改现有客户端代码的情况下在程序中添加新类型的适配器。
  • 代码整体复杂度增加。因为你需要新增一系列接口和类。 有时直接更改服务类使其与其他代码兼容会更简单。

8.用到的多态机制

SquarePegAdapter(适配器)继承了的RoundPeg(圆钉),重写了父类的getRadius()方法,当父类指针指向子类时,调用getRadius()方法,此时会调用适配器的getRadius()方法,利用了多态机制。

9.说明模块抽象封装的方法

适配器实现了其中一个对象的接口, 并对另一个对象进行封装。 它在实现客户端接口的同时封装了服务对象。 适配器接受客户端通过适配器接口发起的调用, 并将其转换为适用于被封装服务对象的调用。适配器模式允许你创建一个中间层类, 其可作为代码与遗留类、 第三方类或提供怪异接口的类之间的转换器。还以扩展每个子类, 将缺少的功能添加到新的子类中。

10.分析各个模块的内聚度和模块之间的耦合度

圆钉、方钉、圆孔有各自属性和方法,内聚度高,耦合度低,适配器继承圆孔,与圆孔耦合度高,与其他耦合度低。

11.提供该应用范例完整的源代码包括构建部署

参见https://github.com/yingjiehuangs/adapterModel

猜你喜欢

转载自www.cnblogs.com/yingjiehuang/p/11986363.html