订阅/发布模式(观察者)

5 阅读1分钟

pub/sub这个应该大家用到的最广的设计 模式了 在这种模式中,并不是一个对象调用另一个对象的方法,而是一个对象订阅另一个对象

特定活动并在状态改变后获得通知,订阅者因此也成为观察者,而被观察的对象成为发布者或主题。当发生了一个重要事件的时候发布者会通知(调用)所有订阅者并且可能经常以事件对象的形式传递消息。

简单的实现发布订阅设计模式

// 创建EventBus
class EventBus {
  constructor() {
    // 储存事件
    this.tasks = {};
  }
  // 绑定事件
  $on(eName, cb) {
    typeof cb == "function"
      ? this.tasks[eName] || (this.tasks[eName] = [])
      : this.Error(cb, "is not a function");
    this.tasks[eName].some(fn => fn == cb) 
      ? true 
      : this.tasks[eName].push(cb); // 避免重复绑定
  }
  // 触发事件
  $emit(eName, ...arg) {
    let taskQueue;
    this.tasks[eName] && this.tasks[eName].length > 0
      ? (taskQueue = this.tasks[eName])
      : this.Error(eName, "is not defined or is a array of having empty callback");
    taskQueue.forEach(fn => {
      fn(...arg);
    });
  }
  // 触发一次
  $once(eName, cb) {
    let fn = (...arg) => {
      this.$off(eName, fn);
      cb(...arg);
    };
    typeof cb == "function" && this.$on(eName, fn);
  }
  // 卸载事件
  $off(eName, cb) {
    let taskQueue;
    this.tasks[eName] && this.tasks[eName].length > 0
      ? (taskQueue = this.tasks[eName])
      : this.Error(eName, "is not exist");
    if (typeof cb === "function") {
      let index = taskQueue.findIndex(v => (v == cb));
      index != -1 &&
        taskQueue.splice(
          taskQueue.findIndex(v => (v == cb)),
          1
        );
    }
    if (typeof cb === "undefined") {
      taskQueue.length = 0;
    }
  }
  // 异常处理
  Error(node, errorMsg) {
    throw Error(`${node} ${errorMsg}`);
  }
}

我们针对自己的模式进行简单的使用:

// 首先定义一个事件池
const EventSinks = {
  add(x, y) {
    console.log("总和: " + x + y);
  },
  multip(x, y) {
    console.log("乘积: " + x * y);
  },
  onceEvent() {
    console.log("我执行一次后就自动卸载");
  }
};
// 实例化对象
let bus = new EventBus();
bus.$on("operator", EventSinks.add); // 监听operator事件, 增加一个EventSinks.add
bus.$on("operator", EventSinks.add); // 当事件名和回调函数相同时,跳过,避免重复绑定
bus.$on("operator", EventSinks.multip); // 给operator事件增加一个EventSinks.multip回调函数
bus.$once("onceEvent", EventSinks.onceEvent); // 触发一次后卸载
console.log(bus.tasks); // { operator: [ [Function: add], [Function: multip] ], onceEvent: [ [Function: fn] ]}
bus.$emit("operator", 3, 5); // 总和:8  乘积:15
bus.$emit("onceEvent"); // 我就执行一次
console.log(bus.tasks); // { operator: [ [Function: add], [Function: multip] ], onceEvent: [] }
bus.$off("operator", EventSinks.add); // 卸载掉operator事件中的EventSinks.add函数体
console.log(bus.tasks); // { operator: [ [Function: multip] ], onceEvent: [] }
bus.$off("operator"); // 卸载operator事件的所有回调函数
console.log(bus.tasks); // { operator: [], onceEvent: [] }
bus.$emit("onceEvent"); // onceEvent is not defined or is a array of having empty callback