【西瓜带你学设计模式 | 第七期 - 适配器模式】适配器模式 —— 类适配器与对象适配器实现、优缺点与适用场景

文章目录

    • 前言
    • [1. 适配器模式是什么?](#1. 适配器模式是什么?)
    • [2. 为什么要用适配器?能解决什么问题?](#2. 为什么要用适配器?能解决什么问题?)
    • [3. 实现思路](#3. 实现思路)
    • [4. 类适配器 vs 对象适配器](#4. 类适配器 vs 对象适配器)
    • [5. 示例一:把"老接口"适配成"新接口"(对象适配器)](#5. 示例一:把“老接口”适配成“新接口”(对象适配器))
      • [5.1 目标接口(客户端期望)](#5.1 目标接口(客户端期望))
      • [5.2 已有类(第三方/老系统:接口不一致)](#5.2 已有类(第三方/老系统:接口不一致))
      • [5.3 适配器(把 String 转成 byte\[\])](#5.3 适配器(把 String 转成 byte[]))
      • [5.4 客户端使用](#5.4 客户端使用)
    • [6. 示例二:常见"返回值适配"(支付/日志/响应统一)](#6. 示例二:常见“返回值适配”(支付/日志/响应统一))
      • [6.1 目标响应](#6.1 目标响应)
      • [6.2 已有类返回老格式](#6.2 已有类返回老格式)
      • [6.3 适配器把 OldPayResult 转成 ApiResponse](#6.3 适配器把 OldPayResult 转成 ApiResponse)
    • [7. 适配器模式常见结构](#7. 适配器模式常见结构)
    • [8. 优缺点](#8. 优缺点)
    • [9. 适用场景](#9. 适用场景)
    • [10. 总结](#10. 总结)

前言

在很多项目里,会遇到这种情况:已经存在一个类/第三方库/老系统接口 ,但它的"输入输出/方法命名/数据结构"跟当前系统想要的不一致。

不可能为了兼容它就把原来的代码大改一遍------于是就需要一个"翻译器"把不匹配的接口对齐。

这就是**适配器模式(Adapter Pattern)**要解决的问题。


1. 适配器模式是什么?

适配器模式:把一个类的接口"包装/转换"成客户端期望的接口,让原本因为接口不兼容而无法一起工作的类能协同工作。

它常见的角色可以理解为:

  • 目标接口(Target):客户端期望调用的方法集合
  • 已有类(Adaptee):手上已经有的、但接口不匹配的类(第三方/老系统)
  • 适配器(Adapter):实现目标接口,并内部调用已有类,把数据/行为进行转换

2. 为什么要用适配器?能解决什么问题?

适配器模式通常在以下场景非常常见:

  1. 第三方接口不符合系统规范
    • 比如第三方库返回 Result<T>,但系统统一用 ApiResponse<T>
  2. 老代码接口需要被新系统"继续使用"
    • 不想重写老系统逻辑,只做外部适配
  3. 同一功能需要对不同来源做兼容
    • 例如支付渠道:微信/支付宝接口不同,但上层都希望统一 pay() 形式

3. 实现思路

适配器模式的"落地流程"一般是:

  1. 定义系统期望的接口(Target)
  2. 找到已有类并分析它与目标接口的差异(参数类型、返回值、单位、字段含义等)
  3. 写一个适配器类(Adapter):
    • 对外实现 Target
    • 内部持有 Adaptee(组合为主)
    • 把参数/返回结果转换成目标格式

4. 类适配器 vs 对象适配器

GoF 里常见两种结构(思想类似):

  • 类适配器(继承方式):Adapter 继承已有类 Adaptee,同时实现 Target
  • 对象适配器(组合方式):Adapter 内部持有 Adaptee 实例,并实现 Target

更推荐"对象适配器",因为更符合组合优于继承,也更灵活。

下面用"对象适配器"展开讲解。


5. 示例一:把"老接口"适配成"新接口"(对象适配器)

5.1 目标接口(客户端期望)

比如客户端希望所有打印机都实现统一接口:

java 复制代码
public interface Printer {
    void print(String text);
}

5.2 已有类(第三方/老系统:接口不一致)

java 复制代码
public class OldPrinter {
    // 老系统要求打印的是字节数组
    public void printBytes(byte[] bytes) {
        System.out.println("OldPrinter printing: " + new String(bytes));
    }
}

5.3 适配器(把 String 转成 byte\[\])

java 复制代码
public class PrinterAdapter implements Printer {
    private final OldPrinter oldPrinter;

    public PrinterAdapter(OldPrinter oldPrinter) {
        this.oldPrinter = oldPrinter;
    }

    @Override
    public void print(String text) {
        byte[] bytes = text.getBytes();          // 参数转换
        oldPrinter.printBytes(bytes);           // 调用已有类
    }
}

5.4 客户端使用

java 复制代码
public class Demo {
    public static void main(String[] args) {
        OldPrinter old = new OldPrinter();

        Printer printer = new PrinterAdapter(old); // 关键:对外统一成 Printer
        printer.print("Hello Adapter!");
    }
}

效果 :客户端完全不需要关心 OldPrinter 怎么打印,只能调用 Printer.print()


6. 示例二:常见"返回值适配"(支付/日志/响应统一)

很多适配器不是只做参数转换,还会做返回值/数据结构转换

6.1 目标响应

java 复制代码
public class ApiResponse<T> {
    public int code;
    public String msg;
    public T data;

    public ApiResponse(int code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
}

6.2 已有类返回老格式

java 复制代码
public class OldPayResult {
    public boolean ok;
    public String reason;
    public String payId;
}

6.3 适配器把 OldPayResult 转成 ApiResponse

java 复制代码
public class PayAdapter {
    private final OldPayService oldPayService;

    public PayAdapter(OldPayService oldPayService) {
        this.oldPayService = oldPayService;
    }

    public ApiResponse<String> pay(String orderId) {
        OldPayResult r = oldPayService.pay(orderId);

        if (r.ok) {
            return new ApiResponse<>(0, "success", r.payId);
        }
        return new ApiResponse<>(-1, r.reason, null);
    }
}

这类适配器的价值在于:让业务层只认识统一协议,而不需要理解第三方细节。


7. 适配器模式常见结构

可以把 Adapter 的工作理解为两件事:

  1. 接口对齐:Adapter 对外暴露客户端需要的接口
  2. 语义/格式转换:Adapter 负责字段、单位、编码、异常模型等差异

8. 优缺点

优点

  • 让原本不兼容的类可以协作(兼容第三方/老系统)
  • 符合开闭原则:不需要修改已有类,只新增适配器
  • 业务层更干净:客户端只依赖统一接口

缺点

  • 类会变多:每个差异都可能带来一个 Adapter
  • 适配器可能成为"翻译逻辑的集中地",需要避免过度膨胀
  • 语义转换如果做得不好,可能隐藏真实差异导致"看似能用但行为不一致"

9. 适用场景

适配器模式适用于:

  • 要复用第三方库/老系统,但接口不匹配
  • 希望把多个来源包装成统一接口
  • 不想改动 Adaptee(或不允许改动)

不太适用于:

  • 差异非常大且改动频繁,适配器会越来越复杂(可能需要重构/替换方案)
  • "适配器"实际上在做大量业务逻辑,应该考虑拆分或调整架构

10. 总结

当发现"已有能力能用,但接口不符合系统期待",就应该考虑适配器模式:用一个 Adapter 做翻译,让系统其余部分保持稳定。

相关推荐
未秃头的程序猿1 天前
Java 26正式发布!这3个新特性,让代码量直接减半
java·后端·面试
小旭Coding1 天前
卧靠!Go 传给前端的 int64 竟然变成了这个?
后端
ZJPRENO1 天前
吃透软件开发六大设计原则,告别烂代码
设计模式
用户298698530141 天前
Word 文档文本查找与替换的 Java 实现方案
java·后端
阿哉1 天前
Nacos 服务发现源码:藏在背后的两套事件机制,90%的人只讲了一半
java
kunge20131 天前
深度剖析Claude Code 的CLAUDE.md加载逻辑
后端·vibecoding
米沙AI1 天前
MSYS2 快速使用版本
后端
Csvn1 天前
Docker 进阶 — 网络模型、数据持久化与多阶段构建
后端
用户4279254051711 天前
《微博开放平台官方CLI开源了:70+API一行搞定,AI Agent原生支持》
后端