巧妙运用观察者模式实现模块解耦

365 阅读2分钟

背景


开发复杂H5游戏页面,会涉及到不同的模块,比如,游戏模块、计分模块、展示模块等等。为了降低各个模块开发者之间沟通和维护成本,同时为了应对产品疯狂多变的需求轰炸,我们可以巧妙运用观察者模式实现模块解耦。

实现一个观察者类 eventEmitter.js

class EventEmitter {
  constructor() {
    this.handlers = {};
  }
  /**
       * 注册事件
       * @param {事件名词} eventName
       * @param {事件执行} callback
       */
  on(eventName, callback) {
    if (!this.handlers[eventName]) {
      this.handlers[eventName] = [];
    }
    this.handlers[eventName].push(callback);
  }
  /**
   * 触发事件
   * @param {事件名词} eventName
   */
  emit(eventName) {
    if (this.handlers && this.handlers[arguments[0]]) {
      for (var i = 0; i < this.handlers[arguments[0]].length; i++) {
        this.handlers[eventName][i].apply(null, [].slice.call(arguments, 1));
      }
    }
  }
  /**
   * 移除事件
   * @param {事件名词} eventName
   * @param {事件} callback
   */
  remove(eventName, callback) {
    if (this.handlers[eventName] instanceof Array) {
      const handlers = this.handlers[eventName];
      for (let i = 0, len = handlers.length; i < len; i++) {
        if (handlers[i] === callback) {
          this.handlers[eventName].splice(i, 1);
          break;
        }
      }
    }
  }
}

export default EventEmitter;

全局观察者

事件名称建议采用 模块-事件 命名

import EventEmitter from './eventEmitter'
window.watcher = new EventEmitter();

// 游戏触发module_a展示
window.watcher.emit('module_a_show', data)

// 模块b监听到模块a展示事件, 触发module_d关闭
window.watcher.on('module_a_show', data => {
    // do your thing
    window.watcher.emit('module_d_close', data)
})
// 模块c监听到模块a展示事件
window.watcher.on('module_a_show', data => {
    // do your thing
})

// 模块d监听到关闭事件
window.watcher.on('module_d_close', data => {
    // do your thing
})

如何移除事件

事件必须要用变量保存

const moduleShowSuccess = data => {
    
}

window.watcher.on('module-show', moduleShowSuccess)
window.watcher.emit('module-show', data)
window.watcher.remove('module-show', moduleShowSuccess)

除了全局观察者,也可以给模块自身添加观察者

import EventEmitter from './eventEmitter'

class Module extends EventEmitter  {
    constructor() {
        super()
    }
}

const module = new Module()

module.on('show', data => {})
module.emit('show', data)

看到这里很多人可能会存在疑问,这不是发布订阅模式吗?

虽然两种模式都存在订阅者和发布者(具体观察者可认为是订阅者、具体目标可认为是发布者),但是观察者模式是由具体目标调度的,而发布/订阅模式是统一由调度中心调的,所以观察者模式的订阅者与发布者之间是存在依赖的,而发布/订阅模式则不会。

两种模式都可以用于松散耦合,改进代码管理和潜在的复用。

很多人为了写模式而写模式,其实只要能够提高开发者效率,降低维护成本,叫什么还重要吗?并不是因为有了设计模式才有了套路,是因为套路多了才有了设计模式。