适配器模式:软件架构中的万能插头,轻松连接不同世界

🎯 设计模式专栏,持续更新中, 欢迎订阅:JAVA实现设计模式
🛠️ 希望小伙伴们一键三连,有问题私信都会回复,或者在评论区直接发言

适配器模式

适配器模式(Adapter Pattern)是一种结构型设计模式,它的作用是将一个类的接口转换为客户端所期望的另一种接口。适配器模式让原本接口不兼容的类能够合作无间,常用于将新系统集成到旧系统中。

形象的例子:电源插座适配器

设想一下,你去旅游的时候发现你带的电子设备插头是两插的,而当地的插座是三插的,这时候你就需要一个插头适配器,它能够将你的两插头转换为三插头,从而使设备能够正常使用。

在这个例子中:

  • 两插头:代表一个已经存在的接口(老接口)。
  • 三插头插座:代表一个新系统的接口(新接口)。
  • 插头适配器:是适配器,将老接口转化为新接口,使得设备能够正常工作。

类适配器

适配器类继承了要适配的类,并且实现了目标接口。

优点:由于使用了继承,可以直接重写父类中的方法,代码简洁。

缺点:Java 中不支持多继承,所以如果适配的类已经继承了其他类,类适配器就无法使用。

// 目标接口:三插电器
interface ThreePlugDevice {
    void powerWithThreePlug();
}

// 被适配的类:两插电器
class TwoPlugDevice {
    public void powerWithTwoPlug() {
        System.out.println("使用两插电源供电");
    }
}

// 类适配器:两插适配三插
class TwoToThreePlugAdapter extends TwoPlugDevice implements ThreePlugDevice {
    @Override
    public void powerWithThreePlug() {
        // 调用两插电器的供电方法
        this.powerWithTwoPlug();
    }
}

// 测试类
public class ClassAdapterTest {
    public static void main(String[] args) {
        ThreePlugDevice device = new TwoToThreePlugAdapter();
        device.powerWithThreePlug(); // 输出:使用两插电源供电
    }
}

类图:

在这里插入图片描述

对象适配器

实现方式:适配器类持有一个要适配的对象,并通过调用这个对象的接口来实现目标接口。

优点:不依赖于多继承,适配器可以适配多个类,只需要持有不同的对象即可,灵活性较高。

缺点:需要通过对象调用接口,层次较深,代码可能稍微复杂一些。

// 目标接口:三插电器
interface ThreePlugDevice {
    void powerWithThreePlug();
}

// 被适配的类:两插电器
class TwoPlugDevice {
    public void powerWithTwoPlug() {
        System.out.println("使用两插电源供电");
    }
}

// 对象适配器:两插适配三插
class TwoToThreePlugAdapter implements ThreePlugDevice {
    private TwoPlugDevice twoPlugDevice;

    public TwoToThreePlugAdapter(TwoPlugDevice twoPlugDevice) {
        this.twoPlugDevice = twoPlugDevice;
    }

    @Override
    public void powerWithThreePlug() {
        // 调用两插电器的供电方法
        twoPlugDevice.powerWithTwoPlug();
    }
}

// 测试类
public class ObjectAdapterTest {
    public static void main(String[] args) {
        TwoPlugDevice twoPlug = new TwoPlugDevice();
        ThreePlugDevice adapter = new TwoToThreePlugAdapter(twoPlug);
        adapter.powerWithThreePlug(); // 输出:使用两插电源供电
    }
}

类图:

在这里插入图片描述

接口适配器

接口适配器模式(也叫作缺省适配器模式默认适配器模式)是适配器模式的另一种变体,适用于当我们只需要实现接口的一部分功能时,而不想为接口中的每个方法都提供实现。

场景举例

比如,你有一个接口定义了多个方法,但是你只对其中几个方法感兴趣,其他的方法对你来说并不需要。那么,如果直接实现接口,你就必须实现所有的方法,即使有的方法是空实现。这时接口适配器模式就很有用,它可以提供一个默认的实现,这样你就不需要为每个方法都写实现,只需要覆盖你关心的那些方法

形象的例子:活动监听器

设想一下,你在编写一个窗口应用程序,用户可以通过鼠标或键盘进行操作。你需要监听这些事件,但你只对鼠标点击感兴趣,而鼠标移动、键盘按键等事件对你无关紧要。这时,如果你实现一个包含所有事件监听方法的接口,你需要实现很多和你不相关的方法。接口适配器模式就像是一个“监听器适配器”,帮你提供了默认的实现,让你只需要专注于鼠标点击事件

接口适配器模式实现方式

接口适配器模式通过创建一个抽象类,这个抽象类实现接口,并为接口中的所有方法提供一个默认实现(通常是空实现)。然后,具体的子类可以根据需要选择性地覆盖其中的某些方法。

// 定义一个接口,包含多个用户行为的方法
interface UserActionListener {
    void onClick();
    void onDoubleClick();
    void onRightClick();
    void onMove();
}

// 定义一个抽象类,实现接口并提供空的默认实现
abstract class UserActionAdapter implements UserActionListener {
    @Override
    public void onClick() {}
    
    @Override
    public void onDoubleClick() {}
    
    @Override
    public void onRightClick() {}
    
    @Override
    public void onMove() {}
}

// 创建一个子类,只关心 onClick() 事件
class ClickAction extends UserActionAdapter {
    @Override
    public void onClick() {
        System.out.println("鼠标单击事件被触发");
    }
}

// 测试类
public class InterfaceAdapterTest {
    public static void main(String[] args) {
        UserActionListener action = new ClickAction();
        action.onClick();          // 输出:鼠标单击事件被触发
        action.onMove();           // 什么也不会发生,因为没有重写 onMove()
    }
}

类图:

在这里插入图片描述

优点

简化类的实现:通过提供一个抽象适配器类,避免实现类必须覆盖所有接口方法,只需要关注自己感兴趣的部分即可。

灵活性高:子类可以按需选择性地覆盖接口中的某些方法,适应不同的需求。

易于扩展:如果接口中方法增多,通过适配器可以更方便地实现

使用场景

  1. 多方法接口的部分实现:当我们需要实现一个包含很多方法的接口,但只关注其中少部分方法时,接口适配器模式非常有用。
  2. 简化回调类:在事件驱动编程中,尤其是像 GUI 或监听器编程,适配器可以简化代码,使我们只关心自己需要处理的事件。

三种适配器模式的区别对比

比较维度类适配器对象适配器接口适配器
实现方式通过继承来实现适配通过组合来实现适配通过抽象类提供默认实现适配
适用场景当需要适配的类和目标接口有较强的关联当需要适配的类和目标接口没有关联,且需要灵活适配多个类当接口有多个方法,而子类只需实现部分方法
灵活性较低,因为 Java 不支持多继承较高,可以适配多个不同类非常高,子类可选择实现任意接口方法
代码复杂度较低,代码直接继承和重写较高,需要组合对象并实现接口适中,使用抽象类提供空实现
是否支持多继承不支持(因为 Java 不支持多继承)支持(通过组合方式实现)不涉及多继承问题,通过抽象类适配
实现对象适配一个类可以适配多个类适配接口,允许选择性实现部分方法
是否修改原类代码不需要修改原类代码,但使用继承不需要修改原类代码,使用对象持有不修改原类或接口,只需继承抽象类
适配器本质适配器是被适配类的子类适配器是独立的类,通过组合持有被适配类适配器是抽象类的子类

适配器模式选择的场景总结:

  1. 类适配器
    • 当你只需要适配一个类,并且你可以使用继承进行扩展时,这种方式最简单。
    • 缺点是受限于 Java 的单继承限制,无法同时继承多个类。
  2. 对象适配器
    • 当你需要适配多个类,或适配类之间没有继承关系时,使用对象适配器更加灵活。
    • 通过组合方式,可以避免多继承的局限性,适应复杂场景。
  3. 接口适配器
    • 当接口中有多个方法,而你只需要实现其中一部分时,这种方式非常有用。
    • 通过抽象类提供默认实现,子类可以选择性实现感兴趣的方法,而不必关心其他方法。

源码中的应用

Java I/O (InputStream 和 Reader)

Java 的 I/O 库广泛使用了适配器模式来适配不同的输入输出流。

例子: InputStreamReader

  • 作用InputStreamReaderInputStream(字节流)适配为 Reader(字符流),这是典型的适配器模式。
  • 实现原理InputStreamReader 通过组合 InputStream 对象,并将字节流转换为字符流,适配了字符流的接口

代码示例:

InputStream input = new FileInputStream("input.txt");
Reader reader = new InputStreamReader(input, "UTF-8");

SpringMVC 框架

例子 1:HandlerAdapter

  • 作用:在 Spring MVC 中,HandlerAdapter 是典型的适配器模式,用于将不同类型的处理器(如 Controller)适配为统一的 Handler 接口,使得框架可以使用统一的方式处理多种类型的请求。
  • 实现原理:不同的控制器类型(如 ControllerHttpRequestHandler 等)通过对应的 HandlerAdapter 实现适配,屏蔽了多种控制器处理请求的差异。

例子 2:DispatcherServlet

  • 作用DispatcherServlet 作为 Spring MVC 的核心,将各种请求路由到不同的控制器,而这些控制器可能具有不同的接口和功能。Spring 通过适配器模式来统一处理这些控制器请求

JDBC

JDBC 是 Java 访问数据库的 API,提供了统一的接口来处理不同的数据库系统。

例子:DriverManager

  • 作用:JDBC 的 DriverManager 是通过适配器模式将不同数据库驱动的实现类适配为统一的 Driver 接口。通过这种方式,不同的数据库(如 MySQL、PostgreSQL、Oracle)可以通过相同的 JDBC API 进行访问。

实现原理:不同的数据库厂商提供自己的 Driver 实现,通过 JDBC 的适配机制,开发者可以用相同的 API 进行操作

代码示例:

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password");

SLF4J(Simple Logging Facade for Java)

SLF4J 是另一个常用的日志框架,类似于 Apache Commons Logging,但更为简洁和高效。

例子:LoggerAdapter

  • 作用:SLF4J 通过适配器模式,将不同的日志实现(如 Log4jLogback 等)统一适配为一个通用的日志接口 Logger。开发者通过统一的接口进行日志调用,而不需要关心底层实现。
  • 实现原理:不同的日志系统提供各自的实现,SLF4J 通过 LoggerAdapter 进行适配,从而实现日志系统的无缝切换。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

coffee_baby

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

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

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

打赏作者

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

抵扣说明:

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

余额充值