适配器模式核心概念
- 适配器模式 (Adapter Pattern)
- 是一种结构型设计模式,它的核心作用是将一个类 / 对象的接口转换成客户端所期望的另一个接口,使得原本因接口不兼容而无法一起工作的类 / 对象能够协同工作;
- 可以把它理解为生活中的电源适配器:比如你有一个港版插头 (原有接口),但家里只有国标插座 (目标接口),适配器就能让两者兼容使用;
- 类图:
- 实现代码:
// ========== 1. Target(目标接口):客户端期望的接口 ========== class Target { /** * 客户端期望的统一方法:请求数据 * @returns {string} 标准化的返回格式 */ request() { throw new Error("子类必须实现 request 方法"); } } // ========== 2. Adaptee(适配者):已有但接口不兼容的类 ========== class Adaptee { /** * 原有接口:格式不符合客户端要求 * @returns {object} 原始数据格式 */ specificRequest() { // 模拟后端返回的非标准化数据(比如老接口返回的格式) return { code: 200, data: { name: "张三", age: 25 }, msg: "success" }; } } // ========== 3. Adapter(适配器):转换接口的核心类 ========== class Adapter extends Target { constructor() { super(); // 组合 Adaptee 实例(适配者) this.adaptee = new Adaptee(); } /** * 实现 Target 的 request 方法,转换 Adaptee 的接口 * @returns {string} 客户端期望的标准化字符串格式 */ request() { // 调用适配者的原有方法 const rawData = this.adaptee.specificRequest(); // 转换格式:将对象转为客户端期望的 JSON 字符串 return JSON.stringify({ status: rawData.code, content: rawData.data, message: rawData.msg }); } } // ========== 客户端调用示例 ========== function clientCode(target) { // 客户端只依赖 Target 接口,无需关心具体实现 const result = target.request(); console.log("客户端收到的标准化数据:", result); } // 使用适配器 const adapter = new Adapter(); clientCode(adapter); // 输出结果: // 客户端收到的标准化数据: {"status":200,"content":{"name":"张三","age":25},"message":"success"}
常用使用场景
API数据适配
-
场景:第三方 API 返回格式与前端期望格式不一致;
-
实现代码:
// 原始API响应 const apiResponse = { user_info: { name_str: '李四', age_num: 30, contact_info: { email_addr: 'lisi@example.com', phone_num: '13800138000' } } }; // 前端期望的数据格式 class UserDataAdapter { static adapt(rawData) { return { name: rawData.user_info.name_str, age: rawData.user_info.age_num, email: rawData.user_info.contact_info.email_addr, phone: rawData.user_info.contact_info.phone_num }; } } // 在Vue组件中使用 const userData = UserDataAdapter.adapt(apiResponse); console.log('适配后的用户数据:', userData);
第三方库适配
-
场景:统一不同日期库的 API;
-
实现代码:
// 使用dayjs import dayjs from 'dayjs'; // 使用moment(假设要迁移到dayjs) import moment from 'moment'; // 日期库适配器 class DateAdapter { constructor(library = 'dayjs') { this.library = library; } format(date, formatStr) { if (this.library === 'dayjs') { return dayjs(date).format(formatStr); } else if (this.library === 'moment') { return moment(date).format(formatStr); } } add(date, amount, unit) { if (this.library === 'dayjs') { return dayjs(date).add(amount, unit).toDate(); } else if (this.library === 'moment') { return moment(date).add(amount, unit).toDate(); } } diff(date1, date2, unit) { if (this.library === 'dayjs') { return dayjs(date1).diff(dayjs(date2), unit); } else if (this.library === 'moment') { return moment(date1).diff(moment(date2), unit); } } } // 使用示例 const dateAdapter = new DateAdapter('dayjs'); console.log('格式化日期:', dateAdapter.format(new Date(), 'YYYY-MM-DD')); console.log('日期差异:', dateAdapter.diff('2024-01-01', '2023-01-01', 'day'));
浏览器API适配
-
场景:兼容不同浏览器的 localStorage;
-
实现代码:
class StorageAdapter { constructor() { this.storage = this.getStorage(); } getStorage() { try { // 尝试使用localStorage if (window.localStorage) { return window.localStorage; } // 降级到cookie return new CookieStorage(); } catch (e) { // 完全降级到内存存储 return new MemoryStorage(); } } setItem(key, value) { this.storage.setItem(key, value); } getItem(key) { return this.storage.getItem(key); } removeItem(key) { this.storage.removeItem(key); } } // Cookie存储适配器 class CookieStorage { setItem(key, value) { document.cookie = `${key}=${value}; path=/; max-age=31536000`; } getItem(key) { const cookies = document.cookie.split('; '); for (let cookie of cookies) { const [cookieKey, cookieValue] = cookie.split('='); if (cookieKey === key) { return cookieValue; } } return null; } removeItem(key) { document.cookie = `${key}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`; } } // 内存存储适配器 class MemoryStorage { constructor() { this.data = new Map(); } setItem(key, value) { this.data.set(key, value); } getItem(key) { return this.data.get(key) || null; } removeItem(key) { this.data.delete(key); } }
适配器模式优缺点
优点
-
单一职责原则:将接口转换代码从业务逻辑中分离;
-
开闭原则:可以引入新类型适配器而无需修改现有代码;
-
提高复用性:让不兼容的类可以一起工作;
-
透明性:客户端通过目标接口操作,无需知道适配器的存在;
缺点
-
代码复杂度增加:需要引入新类和接口;
-
可能影响性能:适配器层会增加调用开销;
-
过度使用导致混乱:过多的适配器会让系统难以理解;
单例模式
上一篇