Java设计模式-适配器模式

Java设计模式-适配器模式

模式概述

适配器模式简介

核心思想:通过定义一个适配器类,将被适配者(Adaptee)的接口转换为客户端期望的目标接口(Target),使得原本因接口不兼容而无法协作的类能够协同工作。本质是"接口转换器",解决不同系统或模块间的接口适配问题。

模式类型:结构型设计模式(关注对象的组成与接口适配)。

作用

  • 解决接口不兼容:让不兼容的接口通过适配器协同工作(如旧系统接口适配新需求)。
  • 复用已有类:无需修改被适配者的代码,直接复用其功能(如集成第三方库)。
  • 提高系统灵活性:通过适配器隔离接口差异,降低模块间耦合(如支持多类型数据源接入)。

典型应用场景

  • 第三方库/服务接口适配(如调用微信支付接口与系统内部支付接口不兼容)。
  • 旧系统升级改造(如将遗留系统的LegacyUserService接口适配为新系统要求的NewUserService接口)。
  • 多数据源整合(如将MySQL的ResultSet适配为Redis的HashOperations接口)。
  • 统一抽象层(如为不同日志框架(Log4j/SLF4J)提供统一日志接口)。

我认为:适配器模式是"接口翻译官",用最小成本让"方言"与"普通话"互通,是系统集成与旧代码改造的必备工具。

课程目标

  • 理解适配器模式的核心思想和经典应用场景
  • 识别应用场景,使用适配器模式解决功能要求
  • 了解适配器模式的优缺点

核心组件

角色-职责表

角色 职责 示例类名
目标接口(Target) 定义客户端期望的接口(如Payment),声明需要实现的方法 Payment
被适配者(Adaptee) 已存在的、需要被适配的类(如第三方支付类WeChatPay),拥有不兼容接口 WeChatPay
适配器(Adapter) 实现目标接口,包装被适配者实例,将其方法转换为目标接口的方法 WeChatPayAdapter

类图

下面是一个简化的类图表示,展示了适配器模式中的主要角色及其交互方式:
实现 持有 使用 创建 创建空 <<interface>> Payment +pay(amount: double) WeChatPay -appId: String +WeChatPay(appId: String) +specificPay(openId: String, amount: double) WeChatPayAdapter -adaptee: WeChatPay -openId: String +WeChatPayAdapter(adaptee: WeChatPay, openId: String) +pay(amount: double) Client +main(args: String[])


传统实现 VS 适配器模式

案例需求

案例背景 :某电商平台需要接入微信支付功能,但微信支付的SDK提供的支付方法是WeChatPay.specificPay(String openId, double amount)(需传入用户openId),而电商系统内部统一的支付接口是Payment.pay(double amount)(仅需金额)。直接调用会导致接口不兼容。

传统实现(痛点版)

代码实现

java 复制代码
// 目标接口:系统内部统一的支付接口
//interface Payment {
//    void pay(double amount);  // 仅需金额
//}

// 被适配者:微信支付SDK(第三方类,不可修改)
class WeChatPay {
    private String appId;  // 微信应用ID(需初始化)

    public WeChatPay(String appId) {
        this.appId = appId;
    }

    // 微信支付的实际方法(需openId和金额,与系统接口不兼容)
    public void specificPay(String openId, double amount) {
        System.out.printf("微信支付:用户%s支付%.2f元%n", openId, amount);
    }
}

// 传统实现:客户端直接处理接口差异(硬编码适配)
public class Client {
    public static void main(String[] args) {
        WeChatPay weChatPay = new WeChatPay("wx_app_123");
        double amount = 99.9;

        // 问题:需在客户端硬编码openId(违反单一职责,且无法复用)
        weChatPay.specificPay("user_openid_456", amount); 
        
        // 若需适配其他支付方式(如支付宝),需重复编写类似代码
    }
}

痛点总结

  • 接口强耦合 :客户端需直接处理被适配者的特殊参数(如openId),违反"依赖倒置原则"。
  • 代码冗余:每个被适配者需在客户端重复编写适配逻辑(如新增支付宝支付需复制粘贴类似代码)。
  • 可维护性差 :若被适配者接口变更(如specificPay参数调整),所有客户端代码需同步修改。

适配器模式 实现(优雅版)

代码实现

java 复制代码
// 1. 目标接口:系统内部统一的支付接口
interface Payment {
    void pay(double amount);  // 客户端仅需传递金额
}

// 2. 被适配者:微信支付SDK(第三方类,不可修改)
class WeChatPay {
    private String appId;

    public WeChatPay(String appId) {
        this.appId = appId;
    }

    // 微信支付的原始方法(需openId和金额)
    public void specificPay(String openId, double amount) {
        System.out.printf("微信支付:用户%s支付%.2f元%n", openId, amount);
    }
}

// 3. 适配器:将微信支付接口适配为系统统一的Payment接口
class WeChatPayAdapter implements Payment {
    private final WeChatPay adaptee;  // 持有被适配者实例
    private final String openId;      // 微信支付需要的额外参数(封装在适配器中)

    public WeChatPayAdapter(WeChatPay adaptee, String openId) {
        this.adaptee = adaptee;
        this.openId = openId;
    }

    @Override
    public void pay(double amount) {
        // 调用被适配者的specificPay方法,传入封装的openId和amount
        adaptee.specificPay(openId, amount); 
    }
}

// 4. 客户端使用:通过适配器间接调用微信支付
public class Client {
    public static void main(String[] args) {
        WeChatPay weChatPay = new WeChatPay("wx_app_123");
        String openId = "user_openid_456";  // 微信用户ID(仅在适配器初始化时传入)
        Payment payment = new WeChatPayAdapter(weChatPay, openId);  // 适配器完成接口转换

        payment.pay(99.9);  // 客户端仅需调用统一接口,无需关心微信支付的细节
        // 输出:微信支付:用户user_openid_456支付99.90元
    }
}

优势

  • 接口解耦 :客户端仅依赖目标接口(Payment),无需感知被适配者的特殊参数(如openId)。
  • 代码复用:适配器封装接口转换逻辑,新增支付方式(如支付宝)只需编写新的适配器类,客户端无感知。
  • 可维护性高 :被适配者接口变更时(如specificPay参数调整),仅需修改适配器类,无需改动客户端代码。

局限

  • 类数量增加 :每个被适配者需定义一个适配器类(如WeChatPayAdapterAlipayAdapter),可能增加系统类膨胀。
  • 过度设计风险:若仅需适配单个接口且调用频率低,直接修改客户端代码可能更简单(需权衡长期维护成本)。
  • 适配器复杂性:若被适配者接口与目标接口差异极大(如方法名、参数类型完全不同),适配器实现可能较复杂。

模式变体

  • 类适配器(Class Adapter) :通过继承被适配者类实现目标接口(需被适配者非final),相比对象适配器更简洁,但受限于单继承(Java/C#不支持多继承)。
  • 对象适配器(Object Adapter):通过组合持有被适配者实例实现目标接口(如上述案例),更灵活(支持多适配),是更常用的变体。
  • 双向适配器(Bidirectional Adapter) :同时实现两个接口(目标接口和被适配者接口),允许两个不兼容的类相互调用(如JavaList适配PythonList)。
  • 缺省适配器(Default Adapter):为目标接口提供默认实现,子类仅需重写需要的方法(如GUI事件监听器,避免实现所有空方法)。

最佳实践

建议 理由
优先使用对象适配器 组合模式更灵活(支持多适配),避免单继承限制(如Java的final类无法被继承)。
适配器职责单一 仅负责接口转换,不添加额外逻辑(如参数校验、业务处理),保持代码清晰。
明确接口契约 目标接口需与客户端期望严格一致,避免适配器隐藏关键逻辑导致调试困难。
封装被适配者的依赖 适配器初始化时传入被适配者实例(如WeChatPay),而非在适配器内部创建,提高测试灵活性(支持Mock)。
添加单元测试 验证适配器的接口转换逻辑(如输入金额是否正确传递给被适配者)。

一句话总结

适配器模式通过"接口转换"解决了不同系统或模块间的接口不兼容问题,是复用已有功能、降低系统耦合的关键工具,尤其适用于集成第三方库或旧系统改造场景。

如果关注Java设计模式内容,可以查阅作者的其他Java设计模式系列文章。😊

相关推荐
超级小忍2 分钟前
从零开始:JDK 在 Windows、macOS 和 Linux 上的下载、安装与环境变量配置
java·windows·macos
.鸣10 分钟前
Java学习笔记:IDEA简单使用技巧
java·学习
2501_9167665414 分钟前
【IDEA2017】使用设置+创建项目的不同方式
java·intellij-idea
kyle~14 分钟前
C++---多态(一个接口多种实现)
java·开发语言·c++
funfan051718 分钟前
IDEA基础配置优化指南(中英双版)
java·ide·intellij-idea
罗小爬EX19 分钟前
在IDEA中设置新建Java类时自动添加类注释
java·intellij-idea
vvilkim34 分钟前
深入理解 Spring Boot Starter:简化依赖管理与自动配置的利器
java·前端·spring boot
柯南二号41 分钟前
【Java后端】【可直接落地的 Redis 分布式锁实现】
java·redis·分布式
1点东西1 小时前
新来的同事问我当进程/机器突然停止时,finally 到底会不会执行?
java·后端·程序员
Aspartame~2 小时前
K8s的相关知识总结
java·容器·kubernetes