适配器模式适合哪些应用场景?
简化版
适配器模式适合已有类能满足功能但接口不符合当前系统期望的场景,比如第三方 SDK 接入、老接口兼容、新旧系统迁移、多供应商接口统一、数据格式转换、框架扩展点适配和跨模块集成。
详细版
适合使用适配器模式的场景通常具备这些特征:
- 已有对象有可复用能力;
- 客户端期望的是另一种接口;
- 不方便修改已有对象或客户端;
- 需要统一多个外部实现;
- 需要隔离第三方接口变化;
- 需要渐进式迁移旧系统。
典型场景包括:
- 接入支付、短信、地图、存储等第三方 SDK;
- 把老接口包装成新接口;
- 多供应商统一成内部标准接口;
- 把不同版本 API 转换成内部统一模型;
- 字节流和字符流转换;
- 框架把不同用户扩展对象适配成统一执行方式;
- 防腐层中隔离外部系统模型。
不适合的场景是:两个接口业务语义完全不同、没有复用价值、客户端和被适配者都可以直接修改,或者简单调用不值得引入额外层。
完整版教学
一、第三方 SDK 接入
这是适配器最常见的工程场景。
比如系统要接入多个支付渠道:
AliPay SDK
WechatPay SDK
BankPay SDK
每个 SDK 的请求对象、返回对象、异常类型、签名方式都不同。
业务层不应该理解这些差异。可以定义统一接口:
interface PayChannel {
PayResult pay(PayCommand command);
}
然后每个渠道一个适配器:
AliPayAdapter
WechatPayAdapter
BankPayAdapter
这样业务层只和 PayChannel 交互。
二、老接口兼容和系统迁移
老系统改造时,不可能要求所有调用方一次性切换。
适配器可以让新旧接口共存:
老调用方 -> 旧接口适配器 -> 新服务
新调用方 -> 新接口 -> 新服务
或者:
新调用方 -> 新接口 -> 旧系统适配器 -> 老服务
这样可以分阶段迁移,降低上线风险。
三、多供应商接口统一
短信、对象存储、物流、OCR、地图、风控等能力经常有多个供应商。
如果每个业务都直接对接供应商接口,系统会很混乱。
适配器可以把不同供应商统一成内部接口:
StorageService
-> OssStorageAdapter
-> CosStorageAdapter
-> S3StorageAdapter
后续切换供应商时,业务层不需要跟着改。
四、数据格式和协议转换
适配器不只适配方法,也适配数据格式。
比如:
- JSON 转内部对象;
- XML 接口转内部命令;
- 字节流转字符流;
- 外部错误码转内部错误码;
- 外部枚举转内部枚举。
只要本质是“外部形态和内部形态不一致,需要中间转换”,都可以考虑适配器。
五、框架扩展点适配
框架往往希望以统一方式调用用户代码,但用户代码形态可能不同。
比如 Spring MVC 使用 HandlerAdapter 适配不同 Handler,让 DispatcherServlet 不需要关心具体 Handler 怎么执行。
这种场景下,适配器把“多样的用户扩展”纳入“统一的框架流程”。
六、防腐层中的适配器
在领域驱动设计里,防腐层常用适配器隔离外部系统模型。
外部系统可能叫 CustomerDTO,内部领域模型叫 Member。两者字段和语义不完全一致。
适配层负责转换:
ExternalCustomerDTO -> MemberCommand / Member
这样外部系统变化不会直接污染内部领域模型。
七、什么时候别用适配器
如果目标接口和已有接口都可以直接修改,而且改动成本很低,没必要引入适配器。
如果两个接口语义完全不同,也不要靠适配器硬凑。
如果只是一次临时脚本调用,简单转换函数可能就够了。
适配器的价值来自隔离变化和复用能力,而不是为了多写一个类。
八、边界隔离是否值得增加一层
接入 4 家支付供应商时,业务层只依赖 1 个 PaymentGateway;每家各有 1 个 Adapter,新增第 5 家不修改订单核心逻辑。
domain -> PaymentGateway(Target) -> Vendor A/B/C/D adapters -> SDKs
适配器的验收标准不是“调用成功”,而是 Target 的业务语义在转换前后保持成立。参数、单位、时区、精度、空值、错误码和资源所有权都应逐项形成契约样例,否则最危险的错误会以“成功响应”的形式潜伏。
九、语义边界与契约样例验证
| 检查维度 | 应确认的内容 |
|---|---|
| 机制正确性 | 适配器最适合放在变化频繁的外部边界,而不是散落在每个业务方法。 |
| 适用边界 | 仅有一次性、同语义的小调用可直接转换;长期依赖、供应商多或模型变化快时适配层价值更大。 |
| 测试证据 | 注入 Adaptee 测试替身,断言外部请求、返回映射、异常转换和调用次数 |
| 工程代价 | 重点统计字段映射与对象分配成本、外部模型变更影响面,以及多层版本适配造成的认知债务 |
易错点:适配器最适合放在变化频繁的外部边界,而不是散落在每个业务方法。
十、常见误区与追问
- 误区:多供应商统一接口必须覆盖所有供应商功能的并集。 并集会让接口充满可选字段;应抽取业务真正需要的稳定交集,特有能力另建扩展契约。
- 误区:适配器只需要让方法签名能够编译。 真正的适配还包括单位、时区、错误码、空值、资源释放和幂等语义;签名一致不代表行为兼容。
- 追问:协议转换是否都属于 GoF 适配器? 思想相通,但网络网关、序列化器还可能承担路由和安全职责,回答时应说明层次。
- 追问:适配器应该放在哪一层? 通常放在系统边界或防腐层,让业务层只依赖 Target,避免第三方模型和异常向内扩散。
- 追问:适配器测试为什么需要契约样例? 因为类型相同仍可能单位、精度或错误语义不同;契约样例能证明转换后的行为而不只是编译通过。
十一、加强记忆
适配器适合“有能力但接口不合”的场景,尤其是第三方 SDK、老系统迁移、多供应商统一、格式转换和框架扩展点。判断标准是:能复用、不能直接改、需要统一接口、变化不想扩散。语义不一致或简单到没必要抽象时,不要硬套。