适配器模式核心概念

  1. 适配器模式 (Adapter Pattern)
    1. 是一种结构型设计模式,它的核心作用是将一个类 / 对象的接口转换成客户端所期望的另一个接口,使得原本因接口不兼容而无法一起工作的类 / 对象能够协同工作;
    2. 可以把它理解为生活中的电源适配器:比如你有一个港版插头 (原有接口),但家里只有国标插座 (目标接口),适配器就能让两者兼容使用;
  2. 类图:
  3. 实现代码:
    // ========== 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数据适配

  1. 场景:第三方 API 返回格式与前端期望格式不一致;

  2. 实现代码:

    // 原始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);
    

第三方库适配

  1. 场景:统一不同日期库的 API

  2. 实现代码:

    // 使用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适配

  1. 场景:兼容不同浏览器的 localStorage

  2. 实现代码:

    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);
        }
    }
    

适配器模式优缺点

优点

  1. 单一职责原则:将接口转换代码从业务逻辑中分离;

  2. 开闭原则:可以引入新类型适配器而无需修改现有代码;

  3. 提高复用性:让不兼容的类可以一起工作;

  4. 透明性:客户端通过目标接口操作,无需知道适配器的存在;

缺点

  1. 代码复杂度增加:需要引入新类和接口;

  2. 可能影响性能:适配器层会增加调用开销;

  3. 过度使用导致混乱:过多的适配器会让系统难以理解;

打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

你好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. 适配器模式核心概念
  2. 2. 常用使用场景
    1. 2.1. API数据适配
    2. 2.2. 第三方库适配
    3. 2.3. 浏览器API适配
  3. 3. 适配器模式优缺点
    1. 3.1. 优点
    2. 3.2. 缺点