一、什么是适配器模式(Adapter Pattern)?
适配器模式是一种结构型设计模式,其核心目标是:
将一个接口转换成客户端期望的另一个接口,使原本因接口不兼容而不能一起工作的类可以协同工作。
二、为什么要使用适配器模式?
在实际开发中,我们常遇到老接口不能直接用,但又不能直接改源码的情况:
-
使用的第三方库接口和自己系统不兼容
-
老系统接口无法满足新需求,但又不能破坏原有逻辑
-
两个模块由不同团队设计,接口对不上
此时,修改代码代价太高或不可行,最好的办法是加一层“翻译器” → 这正是适配器的职责。
✅ 举例:不同国家的插头
出国旅行时,你的手机充电器是扁头插头 🇨🇳,但酒店插座是圆孔 🇪🇺,怎么办?
你不会砸掉墙上的插座重装,也不会改造你的充电器 ——
✅ 你会用一个“插头适配器”把它们连接起来。
这就是适配器模式。
三、适配器模式适用场景
触发场景 | 原因或需求 |
---|---|
系统之间接口不同,无法直接协作 | 第三方库/老模块/其他团队产出的类接口不一致 |
不允许修改原始类代码 | 原类可能已封装、已部署、已在多个地方使用,不可直接更改 |
希望在不破坏现有代码的基础上,兼容新旧接口 | 新系统上线,需要兼容老版本接口或数据模型 |
希望复用现有功能,但需要对接统一标准 | 统一平台标准接入,不希望重写已有逻辑 |
四、Java 实战:对象适配器模式(基于组合)
✅ 背景设定:客户端期望的是 Target 接口,但现成类是 Adaptee
// 客户端期望的接口
public interface Target {
void request();
}
// 现成类(第三方、老代码)
public class Adaptee {
public void specificRequest() {
System.out.println("来自 Adaptee 的特殊请求");
}
}
✅ 适配器类:桥接 Target 和 Adaptee
// 适配器:将 Adaptee 接口“翻译”为 Target 接口
public class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
// 转调 Adaptee 的方法
adaptee.specificRequest();
}
}
📦 使用方式:
Target target = new Adapter(new Adaptee());
target.request(); // 输出:来自 Adaptee 的特殊请求
✅ 场景说明
假设你原本的系统接口是 Target,但你现在想接入某个 SDK(它只有 specificRequest() 方法),无法直接使用。
适配器封装了差异,客户端依然调用原始方式 request(),无感知。
五、类适配器模式(基于继承)
Java 中支持单继承,因此类适配器只能在单继承情况下使用。
public class ClassAdapter extends Adaptee implements Target {
@Override
public void request() {
specificRequest(); // 直接调用继承来的方法
}
}
📦 使用方式:
Target t = new ClassAdapter(); t.request();
✅ 类适配器 vs 对象适配器
对比维度 | 类适配器(继承) | 对象适配器(组合) |
---|---|---|
灵活性 | 不如组合灵活(受限于单继承) | 高,可适配多个对象 |
可扩展性 | 不易扩展其他适配逻辑 | 可灵活添加桥接逻辑 |
适用场景 | 简单适配,结构固定 | 更通用,推荐使用对象适配器 ✅ |
六、缺点与触发场景
缺点 | 场景说明 |
---|---|
引入适配器增加系统复杂度 | 若接口种类多,适配器数量可能爆炸,代码维护成本升高 |
运行时可能隐藏错误 | 不匹配时逻辑可能被吞掉,运行时报错而非编译时报错 |
破坏系统一致性 | 过多适配器会导致系统风格不统一,新旧接口混用 |
只能适配现有接口 | 无法处理类内部行为差异或副作用,如状态不一致 |
🔍 场景举例(问题触发)
1. 第三方支付 SDK 接入
原系统调用方式:
paymentService.pay(amount);
新接入的 SDK 只有:
sdk.processTransaction(amount, currency, key);
你不能改系统代码,只能适配:
public class SdkPaymentAdapter implements PaymentService {
private NewPaymentSDK sdk;
public void pay(double amount) {
sdk.processTransaction(amount, "CNY", "api-key");
}
}
2. 接口迁移但必须兼容老版本
老代码中:
public interface Logger {
void log(String message);
}
新系统使用:
public class AdvancedLogger {
public void write(String level, String message) { ... }
}
使用适配器保持兼容:
public class LoggerAdapter implements Logger {
private AdvancedLogger advLogger = new AdvancedLogger();
public void log(String message) {
advLogger.write("INFO", message);
}
}
七、适配器模式 VS 装饰器/桥接/代理模式
模式 | 作用焦点 | 是否改变接口 | 是否新增行为 | 典型用途 |
---|---|---|---|---|
适配器 | 接口转换 | ✅ | ❌ | 兼容旧系统/对接第三方接口 |
装饰器 | 增强原有功能 | ❌ | ✅ | 动态功能扩展,增强行为 |
桥接模式 | 解耦抽象与实现 | ✅ | ✅ | 多维度变化、避免类爆炸 |
代理模式 | 控制访问、延迟执行、安全控制等 | ❌ | ✅(控制行为) | 远程调用、缓存、安全等场景 |
八、总结
适配器模式是处理接口不兼容但不能改原类的终极方案。
它优雅地隔离了“调用者”和“被调用者”之间的差异,避免侵入式修改,有效提升系统兼容性和灵活性。
但请注意,适配器滥用会让系统结构臃肿、逻辑分散,因此适当封装、统一命名、归档适配器十分重要。
下一篇将带你深入讲解「桥接模式」的核心思想与进阶用法 👨🏫
如需Java面试题资料,可关注公众号:小健学Java,回复“面试”即可获得!