← 返回题目列表

什么是适配器模式?它解决什么问题?

高频 简单 第 1 / 25 题 更新于 2026/07/28
适配器模式结构型模式接口转换设计模式

简化版

适配器模式是把一个已有类或接口转换成客户端期望的接口,让原本因为接口不兼容而不能一起工作的对象可以协作。它主要解决“已有能力可用,但接口不匹配”的问题,常用于旧系统改造、第三方 SDK 封装、接口统一和兼容历史代码。

详细版

适配器模式属于结构型设计模式,核心思想是“转换接口,而不是重写能力”。

典型场景是:客户端只认识 Target 接口,但现有对象 Adaptee 提供的是另一套接口。我们不希望修改客户端,也不希望改动已有对象,就在中间加一个 Adapter,由适配器实现 Target,内部调用 Adaptee

结构可以理解为:

Client -> Target
             ^
             |
          Adapter -> Adaptee

适配器模式的价值是复用已有代码、隔离外部接口变化、降低系统集成成本。它不负责增强功能,也不负责隐藏复杂子系统;它的重点是让接口“对得上”。

面试时可以举例:电源转换插头、第三方支付接口统一、日志框架适配、老接口迁移到新接口、Java I/O 中字节流到字符流的转换思想。

完整版教学

一、先理解“接口不兼容”是什么问题

假设系统里有一个统一的支付接口:

interface PayService {
    void pay(String orderId, int amount);
}

业务代码都按这个接口调用:

payService.pay(orderId, amount);

后来接入一个第三方支付 SDK,但它的方法长这样:

class ThirdPartyPayClient {
    void createPayment(String tradeNo, String money) {
        // 调用第三方支付
    }
}

它不是不能用,而是接口和系统期望的不一样。直接改业务代码去适配第三方 SDK,会让业务层依赖外部接口细节,后续换 SDK 时更麻烦。

适配器模式就是在中间加一层:

class ThirdPartyPayAdapter implements PayService {
    private final ThirdPartyPayClient client;

    public void pay(String orderId, int amount) {
        client.createPayment(orderId, String.valueOf(amount));
    }
}

业务层仍然调用 PayService,第三方差异被适配器消化。

二、适配器模式的本质是“复用已有能力”

适配器不是重新写一个支付系统,也不是给支付系统额外加很多功能。它只是把已有能力包装成目标接口能接受的形态。

这和现实里的电源转换头很像:笔记本需要三孔插头,墙上只有两孔插座,转换头不发电,只负责把接口对齐。

软件里的适配器也是一样:已有能力在 Adaptee 里,客户端需要的是 Target,适配器负责转换。

三、适配器模式的三个核心角色

适配器模式通常有三个角色:

  1. Target:客户端期望的目标接口;
  2. Adaptee:已经存在但接口不兼容的类;
  3. Adapter:实现 Target,内部调用 Adaptee。

示意图:

Client 只依赖 Target
Adapter 实现 Target
Adapter 内部调用 Adaptee

这样客户端不用知道 Adaptee 的存在。

四、适配器解决的是集成问题,不是抽象所有差异

适配器很适合系统集成,但不要把它理解成万能转换器。

如果两个接口只是参数名称不同、方法名不同、返回值格式略有差异,适配器很好用。

但如果两个系统的业务语义完全不同,比如一个是“预授权支付”,另一个是“立即扣款支付”,只是强行改方法名并不能解决语义差异。此时需要重新梳理业务抽象,而不是只写适配器。

五、适配器常见于哪些地方

常见场景包括:

  • 第三方 SDK 接入;
  • 老接口兼容新接口;
  • 多支付渠道统一;
  • 多短信供应商统一;
  • 日志门面适配不同日志实现;
  • 字节流和字符流转换;
  • 框架中把用户实现适配成框架需要的回调接口。

这些场景共同点是:已有对象能做事,但接口不符合当前系统期望。

六、Target 与 Adaptee 的语义转换

业务要求金额以分传入,第三方接口接收元;订单 12345 分必须转换为 123.45 元,少除以 100 会放大 100 倍。

Client -> Target.pay(cents) -> Adapter(convert /100) -> Adaptee.pay(yuan)

适配器的验收标准不是“调用成功”,而是 Target 的业务语义在转换前后保持成立。参数、单位、时区、精度、空值、错误码和资源所有权都应逐项形成契约样例,否则最危险的错误会以“成功响应”的形式潜伏。

七、语义边界与契约样例验证

检查维度应确认的内容
机制正确性适配的核心是让已有能力满足目标契约,而不是简单改方法名。
适用边界适配器能隔离接口差异,不能掩盖无法等价转换的业务语义;信息丢失时必须拒绝或显式降级。
测试证据注入 Adaptee 测试替身,断言外部请求、返回映射、异常转换和调用次数
工程代价重点统计字段映射与对象分配成本、外部模型变更影响面,以及多层版本适配造成的认知债务

易错点:适配的核心是让已有能力满足目标契约,而不是简单改方法名。

八、常见误区与追问

  • 误区:适配器会修改被适配类源码。 典型适配器在外部包装或继承已有类,不要求修改第三方或遗留实现。
  • 误区:适配器只需要让方法签名能够编译。 真正的适配还包括单位、时区、错误码、空值、资源释放和幂等语义;签名一致不代表行为兼容。
  • 追问:怎样定义 Target 才稳定? 从本系统业务语义出发定义最小契约,不照抄某个供应商的数据模型。
  • 追问:适配器应该放在哪一层? 通常放在系统边界或防腐层,让业务层只依赖 Target,避免第三方模型和异常向内扩散。
  • 追问:适配器测试为什么需要契约样例? 因为类型相同仍可能单位、精度或错误语义不同;契约样例能证明转换后的行为而不只是编译通过。

九、加强记忆

适配器模式要记成“已有能力不能直接用,中间加一层把接口转成客户端想要的样子”。它解决接口不兼容,重点是复用和集成;不是增强功能,也不是隐藏子系统,而是让旧对象、第三方对象或不同接口体系能在统一目标接口下协作。