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(`内存加载 ${data} 到 ${position}`)
  }
}

class HardDrive {
  read(sector, size) {
    console.log(`硬盘读取扇区 ${sector},大小 ${size}`)
    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()
  }
}

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

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

// 复杂的底层实现
class RequestInterceptor {
  process(config) {
    config.headers = config.headers || {}
    config.headers['Authorization'] = `Bearer ${localStorage.getItem('token')}`
    return config
  }
}

class ResponseHandler {
  handle(response) {
    if (response.code !== 200) {
      throw new Error(response.message)
    }
    return response.data
  }
}

class ErrorLogger {
  log(error) {
    console.error('请求错误:', error)
    // 上报错误监控系统
  }
}

// 外观类
class HttpFacade {
  constructor() {
    this.interceptor = new RequestInterceptor()
    this.handler = new ResponseHandler()
    this.logger = new ErrorLogger()
  }

  async request(url, options = {}) {
    try {
      // 请求拦截
      const config = this.interceptor.process({
        url,
        method: 'GET',
        ...options
      })

      // 发送请求
      const res = await fetch(config.url, config)
      const json = await res.json()

      // 响应处理
      return this.handler.handle(json)
    } catch (error) {
      this.logger.log(error)
      throw error
    }
  }

  // 简化的 API
  get(url) {
    return this.request(url)
  }

  post(url, data) {
    return this.request(url, {
      method: 'POST',
      body: JSON.stringify(data)
    })
  }
}

// 使用 - 调用者无需关心内部细节
const http = new HttpFacade()
http.get('/api/users')
http.post('/api/login', { username: 'admin', password: '123' })

浏览器兼容性封装

// 事件处理的外观模式
const EventFacade = {
  addEvent(element, type, handler) {
    if (element.addEventListener) {
      element.addEventListener(type, handler, false)
    } else if (element.attachEvent) {
      // IE8 及以下
      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
    }
  }
}

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

关键点

  • 简化接口:将复杂子系统的多个接口合并为一个简单接口
  • 解耦:客户端与子系统之间通过外观类通信,降低耦合度
  • 不限制访问:外观模式不阻止直接使用子系统,只是提供便捷入口
  • 典型应用:jQuery(DOM 操作)、axios(HTTP 请求)、Lodash(工具函数)
  • 适用场景:为复杂模块提供简单 API、封装浏览器兼容性、统一第三方库调用