适配器模式
1. 核心概念与价值
适配器模式在不改变原有代码的基础上,通过增加一个“中间层”来抹平接口之间的差异。
| 维度 | 描述 |
|---|---|
| 核心意图 | 转换接口。解决“这个东西很好,但我没法直接用”的问题。 |
| 主要优点 | 1. 复用性强:让旧的组件或第三方库能在新系统中继续发光发热; 2. 解耦:客户端代码只关注目标接口,不关心底层实现的变化; 3. 灵活性:可以在适配器中加入逻辑,对数据进行预处理。 |
| 主要缺点 | 如果系统中充斥着大量的适配器,会增加代码的阅读成本,有时直接重构原始代码反而更清晰。 |
2. 经典实战场景️
让我们通过两个最常见的开发场景来理解它的实现。
2.1 第三方 SDK 切换(地图组件示例)
假设你的项目原本使用百度地图,现在要切换到高德地图,但它们的渲染方法名完全不同。
| 角色 | 接口 / 方法名 |
|---|---|
| 旧接口 (BaiduMap) | render(mapData) |
| 新接口 (GaodeMap) | show(data) |
| 目标需求 | 统一使用 render 方法调用。 |
js
// 1. 旧的组件实现
class BaiduMap {
render(mapData) { console.log('开始渲染百度地图...', mapData); }
}
// 2. 新的第三方组件(接口不兼容)
class GaodeMap {
show(data) { console.log('开始渲染高德地图...', data); }
}
// 3. 编写适配器
class GaodeMapAdapter {
constructor() {
this.gaodeMap = new GaodeMap();
}
// 抹平差异:让 show 变成 render
render(mapData) {
this.gaodeMap.show(mapData);
}
}
// 4. 统一调用
const renderMap = (map) => map.render({ city: 'Beijing' });
renderMap(new BaiduMap());
renderMap(new GaodeMapAdapter()); // 成功适配!2.2 后端数据格式适配
前端 UI 组件通常需要特定的数据格式,而后端返回的数据结构可能千奇百怪。
js
// 后端返回的原始数据
const oldData = [{ "day": "Monday", "val": 100 }, { "day": "Tuesday", "val": 200 }];
// UI组件需要的格式: { "Mon": 100, "Tue": 200 }
function dataAdapter(data) {
const map = { "Monday": "Mon", "Tuesday": "Tue" };
return data.reduce((acc, item) => {
acc[map[item.day]] = item.val;
return acc;
}, {});
}
const uiData = dataAdapter(oldData);
console.log(uiData); // { Mon: 100, Tue: 200 }3. 模式对比
适配器模式经常被拿来与代理、装饰器对比。
| 模式 | 核心区别 | 形象比喻 |
|---|---|---|
| 适配器模式 | 转换接口。重点在于解决接口不兼容,让东西能动起来。 | 电源转接头。 |
| 代理模式 | 控制访问。接口不变,重点在于控制谁能用、怎么用。 | 明星经纪人。 |
| 装饰器模式 | 增强功能。接口不变,重点在于给原有对象加“ Buff ”。 | 手机贴膜加壳。 |
4. 常见问题 (FAQ)
4.1 什么时候该用适配器,什么时候该直接重构代码?
| 选择 | 适用场景 |
|---|---|
| 使用适配器 | 1. 原始代码极其复杂,改动风险极大; 2. 原始代码是第三方库或 SDK,你没有修改权限; 3. 只是临时过渡方案。 |
| 直接重构 | 1. 项目处于早期阶段,修改成本低; 2. 接口设计极其不合理,适配器也救不了。 |
4.2 适配器模式会影响性能吗?
- 答:在大多数 JS 应用场景中,多一层函数调用或对象封装带来的性能损耗是微秒级的,完全可以忽略不计。相比之下,它带来的代码可维护性提升要重要得多。
4.3 “包装模式” (Wrapper) 是指适配器吗?
- 答:是的。在许多文献中,适配器模式也被称为 Wrapper 模式,因为它本质上就是给旧接口包了一层新外壳。
4.4 适配器可以适配多个对象吗?
- 答:可以。这被称为组合适配器。你可以创建一个适配器,在内部调用多个不同的类,将它们聚合成一个统一的、更好用的接口暴露给外界。