适配器模式:化解接口不兼容的桥梁设计

适配器模式:化解接口不兼容的桥梁设计

一、模式核心:让不兼容的接口协同工作

在软件开发中,经常会遇到接口不兼容的情况:如旧系统提供的接口无法被新系统直接调用,或第三方库的接口与当前系统设计不匹配。适配器模式(Adapter Pattern) 通过创建一个中间适配器,将一个接口转换为另一个接口,使原本不兼容的类可以协同工作,核心解决:

  • 接口转换:将一个类的接口适配为另一个接口
  • 复用 legacy 代码:在不修改原有代码的前提下,让旧组件兼容新系统

核心思想与 UML 类图(PlantUML 语法)

适配器模式分为对象适配器(组合方式)和类适配器(继承方式),典型的对象适配器结构如下:

二、核心实现:适配旧支付接口到新系统

1. 定义目标接口(新系统期望的接口)

java 复制代码
// 新系统使用的支付接口
public interface PaymentTarget {
    void pay(String orderId, double amount); // 支付方法
}

2. 现有适配者(旧系统的支付接口,方法名不兼容)

java 复制代码
// 旧系统支付接口(方法名为payOld)
public class LegacyPayment {
    public void payOld(String orderId, double amount) {
        System.out.println("旧支付接口处理订单:" + orderId + ",金额:" + amount);
    }
}

3. 创建对象适配器(通过组合适配旧接口)

java 复制代码
public class PaymentAdapter implements PaymentTarget {
    private LegacyPayment legacyPayment; // 持有旧接口对象

    public PaymentAdapter(LegacyPayment legacyPayment) {
        this.legacyPayment = legacyPayment;
    }

    @Override
    public void pay(String orderId, double amount) {
        // 将新接口的pay方法适配为旧接口的payOld方法
        legacyPayment.payOld(orderId, amount);
    }
}

4. 客户端使用适配器(透明调用新接口)

java 复制代码
public class ClientDemo {
    public static void main(String[] args) {
        // 创建旧接口对象
        LegacyPayment legacyPayment = new LegacyPayment();
        // 创建适配器,将旧接口适配为新接口
        PaymentTarget adapter = new PaymentAdapter(legacyPayment);
        // 通过新接口调用支付功能
        adapter.pay("ORDER_20231001", 998.0);
    }
}

三、进阶:实现双向适配器与多接口适配

1. 双向适配器(同时适配两个方向的接口)

java 复制代码
// 新接口(目标接口)
public interface NewService {
    void newMethod();
}

// 旧接口(适配者)
public interface OldService {
    void oldMethod();
}

// 双向适配器
public class BidirectionalAdapter implements NewService, OldService {
    private NewService newService;
    private OldService oldService;

    public BidirectionalAdapter(NewService newService, OldService oldService) {
        this.newService = newService;
        this.oldService = oldService;
    }

    // 适配旧接口到新接口
    @Override
    public void newMethod() {
        oldService.oldMethod();
    }

    // 适配新接口到旧接口
    @Override
    public void oldMethod() {
        newService.newMethod();
    }
}

2. 多接口适配器(适配多个不兼容接口)

java 复制代码
public interface LogService {
    void log(String message);
}

public interface NotifyService {
    void sendNotification(String message);
}

public class LegacySystem {
    public void writeToLog(String logMsg) { /* 旧日志方法 */ }
    public void sendAlert(String alertMsg) { /* 旧通知方法 */ }
}

public class MultiAdapter implements LogService, NotifyService {
    private LegacySystem legacySystem;

    public MultiAdapter(LegacySystem legacySystem) {
        this.legacySystem = legacySystem;
    }

    @Override
    public void log(String message) {
        legacySystem.writeToLog("LOG: " + message);
    }

    @Override
    public void sendNotification(String message) {
        legacySystem.sendAlert("ALERT: " + message);
    }
}

3. 可视化适配流程

graph LR A[客户端调用目标接口] --> B[适配器接收请求] B --> C[适配器转换请求参数] C --> D[调用适配者的特殊接口] D --> E[返回结果给适配器] E --> F[适配器转换结果格式] F --> G[返回给客户端]

四、框架与源码中的适配器实践

1. Spring MVC 适配器(HandlerAdapter)

  • 适配不同类型的处理器(Handler),如ControllerHttpRequestHandler
java 复制代码
// 适配@Controller标注的类
public class AnnotationMethodHandlerAdapter implements HandlerAdapter {
    @Override
    public boolean supports(Object handler) {
        return handler instanceof Controller;
    }

    @Override
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 将请求适配到Controller的方法
        return handleInternal(request, response, (Controller) handler);
    }
}

2. AWT/Swing 事件适配器(Event Adapter)

  • 简化事件监听器的实现,如MouseAdapter提供空方法实现
java 复制代码
// 继承MouseAdapter,仅重写需要的方法
public class MyMouseListener extends MouseAdapter {
    @Override
    public void mouseClicked(MouseEvent e) {
        // 处理点击事件
    }
}

3. 数据库驱动适配器(JDBC Driver)

  • JDBC 驱动将不同数据库的原生接口适配为统一的 JDBC 接口
java 复制代码
// MySQL驱动适配器(简化示例)
public class MySQLDriver implements Driver {
    @Override
    public Connection connect(String url, Properties info) {
        // 适配MySQL的原生连接方法
        return new MySQLConnection(url, info);
    }
}

五、避坑指南:正确使用适配器模式的 3 个要点

1. 优先使用对象适配器(组合优于继承)

  • ❌ 类适配器需要继承适配者类,若适配者是 final 类则无法使用
  • ✅ 对象适配器通过组合适配者对象,更灵活且符合合成复用原则

2. 避免过度适配(保持接口简洁)

  • 适配器应聚焦于接口转换,避免包含复杂业务逻辑
  • 复杂转换逻辑可提取为独立的Converter工具类

3. 明确适配范围(避免职责扩散)

  • 一个适配器应只适配一个适配者或一组相关接口
  • 若需要适配多个无关接口,考虑拆分为多个适配器

4. 反模式:滥用适配器掩盖设计缺陷

  • 若系统中存在大量适配器,可能是接口设计不合理导致,需重新审视架构

六、总结:何时该用适配器模式?

适用场景 核心特征 典型案例
复用遗留系统接口 旧接口与新系统不兼容,但无法修改旧代码 整合 ERP 系统、迁移老旧微服务
使用第三方库 第三方库接口与现有系统设计不匹配 集成支付网关、物流 API
统一不同接口格式 需要将多种接口转换为统一的公共接口 日志系统(适配不同日志格式)

适配器模式通过 "接口转换 + 中间层封装" 的设计,为不兼容的接口搭建了沟通的桥梁,是解决系统集成问题的有效方案。下一篇我们将深入探讨桥接模式,解析如何分离抽象与实现的独立变化,敬请期待!

扩展思考:适配器模式 vs 外观模式

两者都涉及对接口的封装,但核心目标不同:

模式 封装目的 接口关系 典型应用
适配器模式 转换接口格式,解决不兼容问题 适配者与目标接口独立 旧系统对接、API 格式转换
外观模式 提供简化的统一接口 外观接口整合多个子系统接口 框架入口、复杂模块简化调用

理解这种差异,能帮助我们在设计时选择更合适的模式来优化系统结构。

相关推荐
悟能不能悟3 分钟前
springboot怎么将事务设置为pending,等另外一个请求ok了,再做commit
spring boot·后端
benpaodeDD5 分钟前
黑马SpringBoot2自动配置原理
java·spring boot·后端
C雨后彩虹16 分钟前
synchronized高频考点模拟面试过程
java·面试·多线程·并发·lock
用户268516121075628 分钟前
GMP 调度器深度学习笔记
后端·go
J_liaty38 分钟前
SpringBoot深度解析i18n国际化:配置文件+数据库动态实现(简/繁/英)
spring boot·后端·i18n
牧小七39 分钟前
springboot 配置访问上传图片
java·spring boot·后端
Dream it possible!40 分钟前
LeetCode 面试经典 150_二分查找_搜索旋转排序数组(114_33_C++_中等)
c++·leetcode·面试
用户268516121075643 分钟前
GMP 三大核心结构体字段详解
后端·go
一路向北⁢1 小时前
短信登录安全防护方案(Spring Boot)
spring boot·redis·后端·安全·sms·短信登录