← 返回题目列表

适配器模式有哪些角色?调用流程是什么?

高频 简单 第 2 / 25 题 更新于 2026/07/28
适配器模式TargetAdapterAdaptee设计模式

简化版

适配器模式通常包含 Client、Target、Adapter 和 Adaptee 四个角色。Client 面向 Target 编程,Adapter 实现 Target,内部调用 Adaptee,把客户端请求转换成被适配对象能理解的调用。

详细版

适配器模式的角色包括:

  1. Client:客户端,只依赖目标接口;
  2. Target:目标接口,也就是客户端希望使用的接口;
  3. Adaptee:被适配者,已有能力所在,但接口不兼容;
  4. Adapter:适配器,实现 Target,并把调用委托给 Adaptee。

调用流程是:

Client 调用 Target 方法
-> 实际执行 Adapter
-> Adapter 做参数、方法、返回值转换
-> Adapter 调用 Adaptee
-> 结果返回给 Client

面试时要强调:客户端不应该直接依赖 Adaptee,否则适配器的隔离价值会下降。适配器的目标是让客户端继续按统一接口使用能力,把外部或历史接口差异封装在适配层。

完整版教学

一、Client:客户端只关心目标接口

客户端是使用能力的一方。好的设计里,客户端只面向系统内部统一接口编程。

比如业务代码只认识:

interface MessageSender {
    void send(String phone, String content);
}

业务代码不应该到处写:

aliyunClient.sendSms(...);
tencentClient.sendMessage(...);

如果业务层直接依赖多个供应商接口,系统会很快被外部 SDK 绑住。

二、Target:目标接口定义系统希望的样子

Target 是客户端真正依赖的抽象。

它应该从本系统业务视角出发设计,而不是照搬某个第三方接口。

比如短信发送接口可以设计成:

interface SmsSender {
    SendResult send(String phone, String templateCode, Map<String, String> params);
}

这个接口表达的是“系统要发送短信”,而不是“某个供应商 SDK 的方法长什么样”。

三、Adaptee:被适配者提供已有能力

Adaptee 是已经存在的对象。它可能是:

  • 老系统类;
  • 第三方 SDK;
  • 遗留接口;
  • 不同协议的客户端;
  • 另一个模块暴露的服务。

它的问题不是没有能力,而是接口和 Target 不一致。

比如:

class AliyunSmsClient {
    AliyunResponse sendTemplateSms(AliyunSmsRequest request) {
        // 调阿里云短信
    }
}

这个类能发送短信,但它不是 SmsSender

四、Adapter:适配器连接两边

Adapter 实现系统目标接口,内部持有被适配者:

class AliyunSmsAdapter implements SmsSender {
    private final AliyunSmsClient client;

    public SendResult send(String phone, String templateCode, Map<String, String> params) {
        AliyunSmsRequest request = new AliyunSmsRequest();
        request.setPhone(phone);
        request.setTemplateCode(templateCode);
        request.setParams(params);

        AliyunResponse response = client.sendTemplateSms(request);
        return convert(response);
    }
}

这里的关键不只是调用转发,还包括参数转换、返回值转换、异常转换和语义对齐。

五、调用流程要体现“客户端不知道被适配者”

完整调用关系应该是:

OrderService -> SmsSender -> AliyunSmsAdapter -> AliyunSmsClient

OrderService 不知道阿里云 SDK 的请求对象、响应对象、异常类型。以后换成腾讯云短信,只需要新增或替换适配器。

这就是适配器模式的隔离价值。

六、适配器里可以做哪些转换

适配器常见转换包括:

  • 方法名转换;
  • 参数格式转换;
  • 单位转换,比如分和元;
  • 枚举值转换;
  • 返回值包装;
  • 异常类型转换;
  • 默认值填充;
  • 协议对象转换。

但适配器不应该承担过多业务决策。复杂业务规则应该放在业务服务或领域层,而不是藏在适配层。

七、四个角色的数据和异常边界

Client 只传 1 个 DomainRequest,Adapter 将其拆成供应商 3 个字段,并把 7 种外部错误码归并为 3 类领域异常。

Client -> Target -> Adapter(map request/error) -> Adaptee

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

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

检查维度应确认的内容
机制正确性客户端不知道 Adaptee,不代表适配层可以吞掉关键失败信息。
适用边界Adapter 可转换参数、返回值、异常和生命周期,但核心业务决策应留在领域服务。
测试证据注入 Adaptee 测试替身,断言外部请求、返回映射、异常转换和调用次数
工程代价重点统计字段映射与对象分配成本、外部模型变更影响面,以及多层版本适配造成的认知债务

易错点:客户端不知道 Adaptee,不代表适配层可以吞掉关键失败信息。

九、常见误区与追问

  • 误区:Adaptee 必须实现 Target。 被适配者通常接口不兼容,正因为不实现 Target 才需要 Adapter 居中转换。
  • 误区:适配器只需要让方法签名能够编译。 真正的适配还包括单位、时区、错误码、空值、资源释放和幂等语义;签名一致不代表行为兼容。
  • 追问:异常应该原样抛给业务层吗? 应保留诊断原因并转换为稳定领域异常,避免业务代码依赖供应商异常类型。
  • 追问:适配器应该放在哪一层? 通常放在系统边界或防腐层,让业务层只依赖 Target,避免第三方模型和异常向内扩散。
  • 追问:适配器测试为什么需要契约样例? 因为类型相同仍可能单位、精度或错误语义不同;契约样例能证明转换后的行为而不只是编译通过。

十、加强记忆

适配器模式的角色可以记成“客户端看 Target,适配器接 Adaptee”。Client 不碰外部接口,Adapter 负责把本系统目标接口转换成被适配者调用。答题时顺着 Client、Target、Adapter、Adaptee 讲,基本就能把结构和价值讲清楚。