适配器模式的优缺点是什么?
简化版
适配器模式的优点是复用已有类、隔离外部接口变化、降低系统集成成本、让客户端面向统一接口编程。缺点是会增加一层间接调用,适配器过多会让结构变复杂;如果接口语义差异很大,强行适配还会掩盖设计问题。
详细版
适配器模式的优点主要有:
- 复用已有能力,不需要重写旧类或第三方 SDK;
- 解耦客户端和被适配对象;
- 统一不同供应商、不同版本、不同模块的接口;
- 支持渐进式迁移和历史兼容;
- 把参数转换、返回值转换、异常转换集中管理。
缺点主要有:
- 增加额外类和调用层次;
- 适配器太多时,调用链路不直观;
- 可能把复杂业务语义差异藏在适配层;
- 类适配器受继承限制,耦合较强;
- 过度使用会让简单问题复杂化。
面试时可以补充:适配器适合解决接口不兼容,不适合解决抽象不合理。如果适配逻辑越来越厚,说明可能需要重新设计领域模型或统一接口标准。
完整版教学
一、优点一:复用已有能力
很多时候已有类已经能完成业务,只是接口不符合当前系统。
适配器可以避免重写:
已有类能力 -> 适配器 -> 系统目标接口
这对接入第三方 SDK、复用老系统、整合不同模块非常有价值。
二、优点二:隔离外部变化
第三方 SDK 经常变化。没有适配层时,业务代码可能到处依赖 SDK 的请求对象和异常类型。
有适配器后,业务层只依赖系统内部接口:
业务层 -> PayService
适配层 -> WxPayClient / AliPayClient
如果微信支付 SDK 升级,主要修改微信支付适配器,不需要大面积改业务代码。
三、优点三:统一多个实现
适配器很适合统一多供应商能力。
比如短信供应商:
SmsSender
-> AliyunSmsAdapter
-> TencentSmsAdapter
-> HuaweiSmsAdapter
业务层统一调用 SmsSender,不同供应商差异由各自适配器处理。
这让切换供应商、灰度路由和降级变得更容易。
四、缺点一:增加间接层
适配器会引入额外类和调用跳转。小系统里如果只有一次简单调用,强行写适配器可能显得啰嗦。
设计模式不是越多越好。只有当接口差异会扩散、外部依赖需要隔离、未来可能切换实现时,适配器才更有价值。
五、缺点二:适配器过多会增加理解成本
一个系统里如果到处都是 Adapter,但命名和职责不清晰,开发者会不知道真正调用了谁。
比如:
OrderAdapter -> PaymentAdapter -> ChannelAdapter -> ClientAdapter
层层适配会让问题排查变困难。
所以适配层要有清晰边界,通常放在基础设施层、防腐层或集成层。
六、缺点三:可能掩盖语义不一致
适配器最危险的用法是只看方法名,不看业务语义。
比如一个接口叫 cancelOrder,另一个接口叫 closeOrder,它们可能不是一回事:
- cancel 可能表示未支付取消;
- close 可能表示关闭交易;
- 是否释放库存、退优惠券、发消息都可能不同。
如果适配器强行把两者映射,短期能跑,长期会产生业务 bug。
七、什么时候应该重新设计而不是继续适配
如果适配器里出现大量业务判断、状态机、事务控制、补偿逻辑,说明它可能已经超出“接口转换”的职责。
这时应考虑:
- 重新抽象目标接口;
- 建立统一领域模型;
- 拆分多个更明确的适配器;
- 把业务规则移到应用服务;
- 给外部系统建立防腐层。
八、隔离收益与转换成本的交换
5 个调用方直接依赖 3 家 SDK 最多形成 15 组耦合;引入 3 个 Adapter 后,调用方只依赖 1 个 Target。
coupling: clients x vendors -> clients -> Target -> vendor adapters
适配器的验收标准不是“调用成功”,而是 Target 的业务语义在转换前后保持成立。参数、单位、时区、精度、空值、错误码和资源所有权都应逐项形成契约样例,否则最危险的错误会以“成功响应”的形式潜伏。
九、语义边界与契约样例验证
| 检查维度 | 应确认的内容 |
|---|---|
| 机制正确性 | 适配器减少依赖扩散,却不会让真实语义差异消失。 |
| 适用边界 | 好处是复用和隔离变化,代价是多一层对象、映射代码和排障路径;高频大对象转换还要测 CPU 与分配。 |
| 测试证据 | 注入 Adaptee 测试替身,断言外部请求、返回映射、异常转换和调用次数 |
| 工程代价 | 重点统计字段映射与对象分配成本、外部模型变更影响面,以及多层版本适配造成的认知债务 |
易错点:适配器减少依赖扩散,却不会让真实语义差异消失。
十、常见误区与追问
- 误区:适配器层越厚,系统隔离越好。 厚到包含核心决策和流程编排时会成为新业务层,应保持边界转换职责。
- 误区:适配器只需要让方法签名能够编译。 真正的适配还包括单位、时区、错误码、空值、资源释放和幂等语义;签名一致不代表行为兼容。
- 追问:什么时候应停止继续加适配器? 当目标模型本身错误或多层适配无法保持语义时,应重设计契约并安排迁移。
- 追问:适配器应该放在哪一层? 通常放在系统边界或防腐层,让业务层只依赖 Target,避免第三方模型和异常向内扩散。
- 追问:适配器测试为什么需要契约样例? 因为类型相同仍可能单位、精度或错误语义不同;契约样例能证明转换后的行为而不只是编译通过。
十一、加强记忆
适配器的优点是复用、解耦、统一接口、隔离变化;缺点是增加间接层、过多会难排查、语义差异可能被掩盖。答题时要强调:适配器适合接口不兼容,不适合把业务语义不一致的问题硬包装成一致。