七、适配器模式


1 基本介绍

适配器模式(Adapter Pattern)是一种 结构型 设计模式,它允许将 一个类的接口 转换成 客户端所期待的另一种接口形式,从而 填补 “现有的程序”和“所需的程序”之间的差异。

适配器模式共有以下两种子模式:

  • 类适配器模式:使用 继承 的关系编写适配器的代码。
  • 对象适配器模式:使用 聚合 的关系编写适配器的代码。

2 案例

现如今,M 芯片的苹果笔记本 Mac 上 只有雷雳接口(Thunderbolt),如果想要使用 USB 接口的设备,要么给 Mac 电脑 插上 转换器,要么就 DIY 一台具有雷雳接口的设备。

2.1 类适配器模式

类适配器模式指出:使用 继承 的关系编写适配器的代码。也就是说适配器需要 继承自 “现有的程序”,即 DIY 一台具有雷雳接口的设备。

2.1.1 Mac 类

public class Mac { // Mac 电脑
    public void connectThunderboltDevice() { // 连接 具有雷雳接口的 设备
        System.out.println("[连接]具有雷雳接口的设备");
    }

    public void useThunderboltDevice() { // 使用 具有雷雳接口的 设备
        System.out.println("[使用]具有雷雳接口的设备");
    }

    public void disconnectThunderboltDevice() { // 断开 具有雷雳接口的 设备
        System.out.println("[断开]具有雷雳接口的设备");
    }
}

2.1.2 USB 接口

// 由于 Java 是单继承,DIYMac 类继承了 Mac 类之后就不能继承 USB 抽象类了,所以将 USB 定义为接口
public interface USB { // USB 接口,USB 设备需要实现这三个方法
    void connectUSBDevice(); // 连接 USB 设备
    void useUSBDevice(); // 使用 USB 设备
    void disconnectUSBDevice(); // 断开 USB 设备
}

2.1.3 DIYUSB 类

public class DIYUSB extends Mac implements USB { // DIY 的 USB 设备
    @Override
    public void connectUSBDevice() {
        System.out.print("通过 DIY 的雷雳接口,");
        super.connectThunderboltDevice();
    }

    @Override
    public void useUSBDevice() {
        System.out.print("通过 DIY 的雷雳接口,");
        super.useThunderboltDevice();
    }

    @Override
    public void disconnectUSBDevice() {
        System.out.print("通过 DIY 的雷雳接口,");
        super.disconnectThunderboltDevice();
    }
}

2.1.4 Client 类

public class Client { // 使用 DIY 的 USB 设备
    public static void main(String[] args) {
        USB usb = new DIYUSB();
        usb.connectUSBDevice();
        usb.useUSBDevice();
        usb.disconnectUSBDevice();
    }
}

2.1.5 Client 类的运行结果

通过 DIY 的雷雳接口,[连接]具有雷雳接口的设备
通过 DIY 的雷雳接口,[使用]具有雷雳接口的设备
通过 DIY 的雷雳接口,[断开]具有雷雳接口的设备

2.2 对象适配器模式

对象适配器模式指出:使用 聚合 的关系编写适配器的代码。也就是说适配器需要 使用 “现有的程序”,即给 Mac 电脑 插上 转换器。

2.2.1 Mac 类

public class Mac { // Mac 电脑
    public void connectThunderboltDevice() { // 连接 具有雷雳接口的 设备
        System.out.println("[连接]具有雷雳接口的设备");
    }

    public void useThunderboltDevice() { // 使用 具有雷雳接口的 设备
        System.out.println("[使用]具有雷雳接口的设备");
    }

    public void disconnectThunderboltDevice() { // 断开 具有雷雳接口的 设备
        System.out.println("[断开]具有雷雳接口的设备");
    }
}

2.2.2 USB 接口

public abstract class USB { // USB 接口,USB 设备需要实现这三个方法
    public abstract void connectUSBDevice(); // 连接 USB 设备
    public abstract void useUSBDevice(); // 使用 USB 设备
    public abstract void disconnectUSBDevice(); // 断开 USB 设备
}

2.2.3 Converter 类

public class Converter extends USB { // 转换器
    private Mac mac;

    public Converter(Mac mac) { // 给 Mac 电脑插上转换器
        this.mac = mac;
    }

    @Override
    public void connectUSBDevice() {
        System.out.print("通过转换器,");
        mac.connectThunderboltDevice();
    }

    @Override
    public void useUSBDevice() {
        System.out.print("通过转换器,");
        mac.useThunderboltDevice();
    }

    @Override
    public void disconnectUSBDevice() {
        System.out.print("通过转换器,");
        mac.disconnectThunderboltDevice();
    }
}

2.2.4 Client 类

public class Client { // 使用转换器
    public static void main(String[] args) {
        USB usb = new Converter(new Mac());
        usb.connectUSBDevice();
        usb.useUSBDevice();
        usb.disconnectUSBDevice();
    }
}

2.2.5 Client 类的运行结果

通过转换器,[连接]具有雷雳接口的设备
通过转换器,[使用]具有雷雳接口的设备
通过转换器,[断开]具有雷雳接口的设备

2.3 总结

如果使用过 Mac 电脑,就会觉得 第二种方案——对象适配器模式 比较好,因为大部分外接设备都是 USB 接口,而自己无法 DIY,所以只能买转换器(拓展坞)来解决这个问题。

在设计时,对象适配器模式 确实比 类适配器模式 好,因为 合成复用原则 指出:不推荐使用 继承 的关系,如果能使用其他关系(聚合组合)来解决问题,就使用其他关系。

3 各角色之间的关系

3.1 角色

3.1.1 Adaptee ( 被适配者 )

该角色是 持有既定方法 的角色。在本例中,Mac 类扮演该角色。假如你买了一台 Mac 电脑之后,Mac 电脑的雷雳接口就是既定的方法。

3.1.2 Target ( 目标 )

该角色负责 定义 所需的 方法。在本例中,USB 接口/抽象类 扮演该角色。对于一个设备,如果它是 USB 设备,就必须实现 USB 所定义的抽象方法。

3.1.3 Adapter ( 适配器 )

该角色负责 使用 Adaptee 角色的既定方法 来 满足 Target 角色的需求,如果 Adaptee 角色的既定方法 和 Target 角色的需求一模一样,则无需 Adapter 进行适配。在本例中,DIYUSB 类、Converter 类扮演该角色。

  • 在类适配器模式中,Adapter 角色通过 继承 来使用 Adaptee 角色。
  • 在对象适配器模式中,Adapter 角色通过 聚合 来使用 Adaptee 角色。

3.1.4 Client ( 客户端 )

该角色负责 使用 Target 角色所定义的方法进行具体处理。在本例中,Client 类扮演该角色。

3.2 类图

3.2.1 类适配器模式

alt text

3.2.2 对象适配器模式

alt text

4 注意事项

  • 明确使用场景:当存在两个接口不兼容的类需要一起工作时,可以考虑使用适配器模式。
  • 确保转换的正确性:在适配器中,需要确保从 Adaptee 角色到 Target 角色的转换是正确的,并且转换后的行为符合客户端的期望。
  • 避免过度使用:虽然适配器模式可以提高类的 复用性 和系统的 灵活性,但 过度使用 可能会增加系统的 复杂性维护成本。因此,在使用适配器模式时,需要权衡其利弊。

5 这种思想在源码中的使用

在 Spring 中,有 面向切面编程(Aspect Oriented Programming,简称 AOP)的概念,虽然 AOP 不直接等同于适配器模式,但可以从以下几个方面看到它们之间的联系:

  • 接口转换:在适配器模式中,适配器通过实现一个接口来转换不兼容的接口。在 AOP 中,虽然不直接涉及接口的转换,但 AOP 代理实现了与被代理对象相同的接口,从而能够在不修改原有代码的情况下,将切面逻辑织入到业务逻辑中。这种代理机制在某种程度上可以看作是接口的一种“动态转换”。
  • 功能增强:适配器模式通过 添加新的行为 来增强现有类的功能。同样,AOP 也是通过 切面 来增强业务方法的功能,如添加日志、事务管理等。不过,AOP 的增强是在运行时 动态 实现的,而适配器模式的增强则通常是 在编译时确定 的。
  • 解耦:适配器模式通过引入 一个独立的适配器类 来解耦 不兼容的接口,从而降低系统的耦合度。AOP 通过将 横切关注点业务逻辑 分离,也达到了类似的解耦效果,使得系统更加灵活和可扩展。

6 优缺点

优点

  • 提高类的复用性:通过适配器模式,可以将一个类的接口转换成另一个不兼容的接口,从而使得原本不能一起工作的类可以一起工作,提高了类的 复用性
  • 降低系统的耦合度:适配器模式使得 客户端代码具体的类 实现解耦,客户端通过适配器与抽象接口交互,避免了客户端与具体实现类的直接依赖。
  • 增强系统的灵活性:通过添加新的适配器类,可以很容易地扩展系统的功能,而不需要修改原有的代码,提高了系统的 灵活性
  • 符合开闭原则:适配器模式遵循开闭原则,即对扩展开放,对修改关闭。当需要增加新的功能时,可以通过添加新的适配器类来实现,而 不需要修改原有的代码

缺点

  • 增加系统的复杂性:使用适配器模式会 增加系统的类数量,从而增加系统的复杂性。如果在一个系统中 过度使用 适配器模式,可能会导致接口和类之间的关系变得复杂和混乱,增加了系统的理解难度和维护成本。
  • 可能隐藏了真实的业务逻辑:在某些情况下,适配器模式可能会隐藏一些真实的业务逻辑,使得系统的 业务逻辑变得不够直观和清晰。这可能会增加系统的复杂性和维护难度。
  • 可能引入性能问题:适配器模式可能需要通过转换接口或数据格式来进行适配,这可能会引入额外的性能开销。特别是在需要频繁进行接口转换的场景下,性能问题可能会更加明显。

7 适用场景

  • 接口不兼容:当有一个已经存在的类(这个类可能是框架中的类),并且这个类的 接口(方法签名)目的 不符合我们当前系统的需求,但是我们又希望能够在不修改原有类代码的基础上,让这个类能够与新系统协同工作时,可以使用适配器模式。通过创建一个适配器类,将原有类的接口转换成新系统所需要的接口,或者使用既定方法的结果达成新的目的。
  • 迁移旧系统到新系统:在进行系统迁移时,旧系统的接口可能与新系统的接口不兼容。但是,由于种种原因(如时间、成本等),我们可能无法立即修改旧系统的代码。这时,可以通过适配器模式来创建一个适配器,使得 旧系统 能够通过这个适配器与 新系统 交互,从而实现 平滑迁移
  • 类的实现与接口分离:有时候,我们可能希望将类的实现与接口分离,以便在不同环境中使用同一个类的不同接口。通过适配器模式,我们可以为同一个类创建多个适配器,每个适配器都实现了不同的接口,从而满足不同的需求。
  • 遵循开闭原则:在软件设计中,开闭原则是一个重要的原则,它要求软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。通过使用适配器模式,我们可以在不修改原有代码的基础上,通过增加新的适配器类来扩展系统的功能,从而遵循开闭原则。
  • 多源数据整合:在数据整合的场景中,可能会遇到来自不同源的数据,这些数据可能具有不同的格式和接口。通过适配器模式,我们可以为每种数据源创建一个适配器,将这些数据源的数据转换成统一的格式和接口,从而方便后续的数据处理和分析。

8 总结

适配器模式是一种 结构型 设计模式,它允许将 一个类的接口 转换成 客户端所期待的另一种接口形式,主要适用于接口不兼容、迁移旧系统、多源数据整合等场景,通过创建适配器类来实现接口的转换,从而提高系统的 复用性灵活性可维护性

其中,有两个子模式:类适配器模式对象适配器模式。推荐使用第二种模式,使用 聚合 来填补“现实”(已有代码)与“理想”(所需功能)的不平衡,因为第二种模式满足 合成复用原则

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值