观察者模式和发布订阅模式

92 阅读1分钟

观察者模式和发布订阅模式

观察者模式

观察者模式存在一对多依赖关系,多个观察者订阅一个对象,当该对象发生变化时所有观察者都会收到通知

首先定义一个观察者类,通过构造函数声明实例化的观察者名称,和接收通知时执行的回调函数

由于观察者模式是一对多的依赖关系

观察者只能被动的接收目标对象的通知,所以实例化时的回调函数可以很好的帮助不同的观察者在接收到通知时执行不同的逻辑

在使用时如果逻辑单一,所有观察者在订阅了某个目标对象时不需要做不同逻辑,则可以在构造函数中除去,直接类中写相应逻辑

    /**
     * 观察者
     */
    class Observer {
      /**
       * @param { string } name 观察者名称
       * @param { Function } callback 接受通知时执行的回调函数
       */
      constructor(name,callback){
        this.name = name
        this.handleTask = callback
      }

      /**
      * 接收目前对象的通知
      * @param {*} task 
      */
      update(task){
        this.handleTask(task)
      }
    }

然后定义一个目标对象类,观察者可以订阅目标对象,订阅后目标对象就可以对其进行通知

/**
* 目标对象
*/
class Subject{
  constructor(name){
    this.ObserverList = []
    this.name = name
  }

  /**
   * 添加订阅者
   * @param { * } observer 
   */
  addObserver(observer){
    // 过滤重复订阅者
    const filter = this.ObserverList.filter(ob => ob.name === observer.name )
    if(filter.length > 0){
      filter.map((item) => {
        console.error(`${this.name}有重复订阅者${item.name}`)
      })
      return
    }
    this.ObserverList.push(observer)
  }

  /**
   * 通知订阅者
   * @param {*} task 
   */
  notify(task){
    this.ObserverList.forEach((observer) => {
      observer.update(task)
    })
  }
}

使用示例

  const sub = new Subject('sub')
  const observer1 = new Observer('observer1',(task) => {
    const {action,data} = task
    if(action === 'notifyAll'){
      console.log(`观察者2号:${data}`)
    }
  })
  const Observer2 = new Observer('observer2',(task) => {
    const {action,data} = task
    if(action === 'notifyAll'){
      console.log(`观察者3号:${data}`)
    }
    if(action === 'gogogo'){
      console.log(`观察者3号:${data}`)
    }
  })
  sub.addObserver(observer)
  sub.addObserver(observer1)
  sub.addObserver(Observer2)
  sub.notify({action:'notifyAll',data:'通知所有观察者'})
  // 观察者1号:通知所有观察者
  // 观察者2号:通知所有观察者
  // 观察者3号:通知所有观察者
  sub.notify({action:'gogogo',data:'进攻!'})
  // 观察者3号:进攻!

发布订阅模式

观察者和订阅者不存在依赖关系,而是通过调度中心分发

  /**
  * 调度中心
  */
  class Dispatch {
    constructor(){
      // 调度中心列表,用于存储订阅者
      this.DispatchList = []
    }

    /**
     * 发布消息
     */
    Publish(event,...args) {
      const fns = this.DispatchList[event]
      if(!Array.isArray(fns)){
        return
      }
      fns.forEach((fn) => {
        fn.call(this,...args)
      })
    }

    /**
     * 订阅消息
     * @param { string } 订阅事件
     * @param { Function } 触发函数
     */
    subscribe(event,fn){
      (this.DispatchList[event] || (this.DispatchList[event] = [])).push(fn)
    }

    /**
     * 取消订阅
     */
    unSubscribe(event,fn){
      if(!Array.isArray(this.DispatchList[event])){
        return
      }
      // 参数fn不为函数则该订阅事件的订阅者全部清空
      if(typeof fn !== 'function'){
        this.DispatchList[event] = []
      }
      const index = this.DispatchList[event].indexOf(fn)
      if(index !== -1){
        this.DispatchList[event].splice(index,1)
      }
    }
  }

订阅,取消订阅和发布消息都通过调度中心来进行,示例代码

  const test = new Dispatch()

  function fn(data){
    console.log(`函数1,${data}`)
  }

  function fn1(data){
    console.log(`函数2,${data}`)
  }

  test.subscribe('test',fn)
  test.subscribe('test',fn1)
  test.Publish('test','进攻!')
  // 函数1,进攻!
  // 函数2,进攻!
  test.unSubscribe('test',fn)
  test.Publish('test','进攻!')
  // 函数2,进攻!