外观模式

用统一接口简化复杂子系统的调用

问题

什么是外观模式?如何在前端开发中应用?

解答

外观模式(Facade Pattern)为复杂的子系统提供一个简单的统一接口,隐藏内部复杂性。

基本示例

// 复杂的子系统
class CPU {
  freeze() {
    console.log('CPU 冻结');
  }
  jump(position) {
    console.log(`CPU 跳转到 ${position}`);
  }
  execute() {
    console.log('CPU 执行指令');
  }
}

class Memory {
  load(position, data) {
    console.log(`内存加载数据到 ${position}`);
  }
}

class HardDrive {
  read(sector, size) {
    console.log(`硬盘读取扇区 ${sector}`);
    return 'boot data';
  }
}

// 外观类 - 提供简单接口
class ComputerFacade {
  constructor() {
    this.cpu = new CPU();
    this.memory = new Memory();
    this.hardDrive = new HardDrive();
  }

  // 一键启动,隐藏复杂的启动流程
  start() {
    this.cpu.freeze();
    this.memory.load(0, this.hardDrive.read(0, 1024));
    this.cpu.jump(0);
    this.cpu.execute();
    console.log('电脑启动完成');
  }
}

// 使用
const computer = new ComputerFacade();
computer.start(); // 一行代码完成复杂操作

前端实际应用:HTTP 请求封装

// 底层 API(复杂)
class XMLHttpRequestHandler {
  send(method, url, data, headers) {
    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      xhr.open(method, url);
      Object.keys(headers || {}).forEach(key => {
        xhr.setRequestHeader(key, headers[key]);
      });
      xhr.onload = () => resolve(JSON.parse(xhr.responseText));
      xhr.onerror = reject;
      xhr.send(JSON.stringify(data));
    });
  }
}

class AuthManager {
  getToken() {
    return localStorage.getItem('token');
  }
}

class ErrorHandler {
  handle(error) {
    console.error('请求错误:', error);
    // 统一错误处理逻辑
  }
}

// 外观类 - 简化 HTTP 请求
class HttpFacade {
  constructor() {
    this.xhr = new XMLHttpRequestHandler();
    this.auth = new AuthManager();
    this.errorHandler = new ErrorHandler();
  }

  // 统一的请求方法
  async request(method, url, data = null) {
    const headers = {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${this.auth.getToken()}`
    };

    try {
      return await this.xhr.send(method, url, data, headers);
    } catch (error) {
      this.errorHandler.handle(error);
      throw error;
    }
  }

  // 简化的 API
  get(url) {
    return this.request('GET', url);
  }

  post(url, data) {
    return this.request('POST', url, data);
  }

  put(url, data) {
    return this.request('PUT', url, data);
  }

  delete(url) {
    return this.request('DELETE', url);
  }
}

// 使用 - 极其简单
const http = new HttpFacade();
http.get('/api/users');
http.post('/api/users', { name: 'John' });

浏览器兼容性处理

// 外观模式处理浏览器差异
const EventFacade = {
  // 统一事件绑定
  addEvent(element, type, handler) {
    if (element.addEventListener) {
      element.addEventListener(type, handler, false);
    } else if (element.attachEvent) {
      element.attachEvent('on' + type, handler);
    } else {
      element['on' + type] = handler;
    }
  },

  // 统一事件移除
  removeEvent(element, type, handler) {
    if (element.removeEventListener) {
      element.removeEventListener(type, handler, false);
    } else if (element.detachEvent) {
      element.detachEvent('on' + type, handler);
    } else {
      element['on' + type] = null;
    }
  },

  // 统一获取事件对象
  getEvent(event) {
    return event || window.event;
  },

  // 统一阻止默认行为
  preventDefault(event) {
    if (event.preventDefault) {
      event.preventDefault();
    } else {
      event.returnValue = false;
    }
  }
};

// 使用 - 无需关心浏览器差异
EventFacade.addEvent(document.getElementById('btn'), 'click', () => {
  console.log('clicked');
});

关键点

  • 简化接口:将复杂子系统的多个接口合并为一个简单接口
  • 解耦:客户端与子系统解耦,子系统变化不影响调用方
  • 不限制访问:外观模式不阻止直接访问子系统,只是提供便捷入口
  • 常见场景:HTTP 请求封装、浏览器兼容处理、第三方库封装
  • 与适配器区别:适配器转换接口,外观简化接口