代码世界的「万能转接头」:适配器模式的跨界艺术
一、当接口开始「鸡同鸭讲」
想象这样的场景:
你的Type-C接口的MacBook,遇上了古董级的VGA投影仪;
说东北话的张三,要跟讲粤语的李四讨论哲学;
新买的蓝牙耳机,遇到了只支持3.5mm接口的诺基亚...
这些魔幻现实主义的画面,正是适配器模式的用武之地!它就像代码世界的翻译官,让两个互相嫌弃的接口突然开始商业互吹。
二、跨界翻译的两种姿势(UML图)
1. 类适配器:继承式联姻
java
┌─────────────┐ ┌─────────────┐
│ Target │ │ Adaptee │
├─────────────┤ ├─────────────┤
│ +request() │ │ +oldRequest │
└──────△──────┘ └──────┬──────┘
│ ┌─────────┴─────────┐
│ │ │
┌─────────────┐ ▼
│ Adapter │
├─────────────┤
│ +request() │
└─────────────┘
- 通过多重继承实现(Java不支持,但可以用接口+继承模拟)
- 像混血儿继承父母双方的基因
2. 对象适配器:组合式外交
java
┌─────────────┐ ┌─────────────┐
│ Target │ │ Adaptee │
├─────────────┤ ├─────────────┤
│ +request() │ │ +oldRequest │
└──────△──────┘ └──────┬──────┘
│ │
│ ┌─────────▼─────────┐
└───────────┤ Adapter │
├───────────────────┤
│ -adaptee: Adaptee │
│ +request() │
└───────────────────┘
- 持有被适配者的引用(组合优于继承)
- 像外交官带着翻译团队
三、代码翻译官的工作日常
场景:让方形钉子适配圆孔
java
// 圆孔标准接口(Target)
interface RoundHole {
boolean fits(RoundPeg peg);
}
// 方钉(被适配者)
class SquarePeg {
private int width;
public SquarePeg(int width) {
this.width = width;
}
public int getWidth() {
return width;
}
}
// 方钉转圆孔适配器(对象适配器)
class SquarePegAdapter implements RoundHole {
private SquarePeg peg;
public SquarePegAdapter(SquarePeg peg) {
this.peg = peg;
}
@Override
public boolean fits(RoundPeg roundPeg) {
// 将方钉对角线长度作为虚拟直径
double virtualDiameter = peg.getWidth() * Math.sqrt(2);
return virtualDiameter <= roundPeg.getDiameter();
}
}
// 使用示例
SquarePeg squarePeg = new SquarePeg(5);
RoundHole hole = new SquarePegAdapter(squarePeg);
hole.fits(new RoundPeg(7)); // 自动转换判断
四、适配器三兄弟的职场PK
维度 | 类适配器 | 对象适配器 |
---|---|---|
实现方式 | 继承 | 组合 |
灵活性 | 只能适配一个类 | 可以适配多个类 |
代码耦合 | 高(需要了解父类实现) | 低(面向接口) |
适用场景 | 需要重写部分行为 | 多数情况 |
现实类比 | 基因改造人 | 随身翻译器 |
选型指南:
- 需要覆盖被适配者方法 → 类适配器
- 需要适配多个类 → 对象适配器
- 不确定时 → 选对象适配器准没错
五、跨界合作的真实战场
- Java集合框架 :
Collections.list()
把老旧的Enumeration适配成Iterator - Android开发 :
BaseAdapter
统一各种数据源的ListView显示 - Spring MVC :
HandlerAdapter
处理不同类型的Controller - 日志框架:SLF4J的桥接器适配Log4j/JUL/Logback
- 支付接口:统一支付宝/微信/银联的不同API
经典案例:
java
// 把老式Enumeration适配成现代Iterator
Enumeration<String> oldEnum = ...;
Iterator<String> newIterator = new EnumerationIteratorAdapter(oldEnum);
六、防翻车翻译指南
-
不要过度使用:
java// 错误示范:适配器套适配器(俄罗斯套娃警告) new AdapterA(new AdapterB(new AdapterC(originObj)));
-
保持适配器轻量:
java// 好的适配器应该像透明胶带 class GoodAdapter implements Target { private Adaptee adaptee; // 仅持有必要引用 // 简洁的转换逻辑... }
-
优先组合而非继承:
java// 使用对象组合就像带翻译官 // 而继承就像自己学会八国语言(累死)
-
注意性能损耗:
java// 高频调用的场景要考虑转换开销 // 必要时缓存转换结果
-
统一异常处理:
javatry { adaptedObject.operation(); } catch (AdapteeException e) { throw new TargetException("翻译后的异常", e); }
七、跨界总结大会
适配器模式就像代码世界的瑞士军刀:
- ✅ 要:用于接口不兼容的场景
- ✅ 要:优先使用对象适配器
- ❌ 不要:把适配器当万能胶滥用
- ❌ 不要:忘记适配器带来的性能损耗
当你在Java标准库中看到Arrays.asList()
时,请记住------这个把数组转List的神器,正是适配器模式的经典应用!