适配器模式:让不兼容的接口握手言和
说实话,适配器模式是我在实际项目中用得最多的设计模式之一。为什么?因为现实世界里的系统集成,从来都不是理想化的。你写了一个完美的接口,对方团队写了一个完美的接口,但两个接口就是配不上——这时候,适配器就是那个救火队员。
适配器模式的核心思想很简单:将一个类的接口转换成客户期望的另一个接口。说白了,就是给不兼容的双方搭一座桥。
生活中的适配器
你想想看,我们平时用的电源适配器,不就是这个道理吗?墙上的插座是220V交流电,你的手机需要5V直流电。没有适配器,手机就充不了电。适配器模式在软件里干的事,本质上是一样的。
我当年接手过一个遗留系统改造项目。老系统用XML传数据,新系统只认JSON。改老系统?成本太高。改新系统?不可能。最后我写了一个适配器,把XML转成JSON,两边都不动,问题就解决了。
两种实现方式
适配器模式有两种实现方式:类适配器和对象适配器。我个人更推荐对象适配器,原因后面会讲。
类适配器(基于继承)
类适配器通过多重继承来实现。适配器同时继承目标接口和需要适配的类。
// 目标接口
public interface ITarget {
void request();
}
// 需要适配的类
public class Adaptee {
public void specificRequest() {
System.out.println("Adaptee: 执行特殊请求");
}
}
// 类适配器
public class ClassAdapter extends Adaptee implements ITarget {
@Override
public void request() {
// 调用父类的方法
specificRequest();
}
}
嗯,这里要注意:Java不支持多重继承,所以类适配器在Java里只能用单继承+接口实现。C++倒是可以,但用得也不多。
对象适配器(基于组合)
对象适配器通过组合来实现。适配器持有被适配者的引用,而不是继承它。
// 对象适配器
public class ObjectAdapter implements ITarget {
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
// 委托给被适配者
adaptee.specificRequest();
}
}
// 使用示例
public class Client {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
ITarget target = new ObjectAdapter(adaptee);
target.request();
}
}
核心区别:类适配器用继承,对象适配器用组合。组合优于继承,这是我一直坚持的原则。
为什么我推荐对象适配器?
我在项目中踩过类适配器的坑。有一次,我需要适配一个第三方库的类,那个类有几十个方法。如果用类适配器,所有方法都会被继承到适配器里,造成接口污染。而且,如果被适配的类有final方法,继承这条路就堵死了。
对象适配器就没有这些问题:
- 灵活性高:可以适配任意子类,只要传入不同的实例就行
- 低耦合:适配器只依赖接口,不依赖具体实现
- 可测试:被适配者可以轻松被mock
我的习惯:除非你确定被适配的类永远不会变,而且你完全控制它的源码,否则优先选对象适配器。
适配器模式的结构图
下面这张图展示了适配器模式的核心结构。我特意把两种方式都画出来了,方便对比。
实际应用场景
适配器模式在真实项目中随处可见。我列几个典型的:
| 场景 | 说明 | 我的经验 |
|---|---|---|
| 第三方库集成 | 第三方库的接口和你的系统不匹配 | 曾经用适配器封装了三个不同的支付SDK,对外暴露统一接口 |
| 遗留系统改造 | 老系统的接口不能改,新系统需要对接 | 做过一个数据迁移工具,适配器把老数据库的查询结果转成新格式 |
| 统一多个相似接口 | 多个类功能相似但接口不同 | 日志框架的适配,log4j、logback、slf4j之间的转换 |
避坑指南:我曾经犯过一个错误——在适配器里加了业务逻辑。适配器只做接口转换,不要在里面做数据校验、格式转换之外的任何事。否则适配器会变成一个大杂烩,维护成本飙升。
什么时候不该用适配器?
适配器不是万能的。我见过有人滥用适配器,把系统搞得一团糟。以下几种情况,我建议你三思:
- 接口差异太大:如果两个接口的语义完全不同,适配器也救不了。比如一个接口是"保存订单",另一个是"发送邮件",这怎么适配?
- 可以重构的时候:如果你能修改被适配的类,直接改接口比加适配器更干净
- 性能敏感场景:适配器增加了一层调用,虽然开销很小,但在高频调用场景下还是要注意
总结
适配器模式,说白了就是给不兼容的接口当翻译。它不改变现有代码,只做转换。我个人觉得,这是设计模式里性价比最高的一个——简单、实用、不容易出错。
记住两个要点:优先用对象适配器,不要在适配器里加业务逻辑。做到这两点,适配器模式就是你手里一把趁手的工具。