timelessover/blog

设计模式专题之发布-订阅模式(四)

Opened this issue · 0 comments

发布订阅模式(Pub-Sub)

定义: 发布者的消息发送者不会将消息直接发送给订阅者的特定接收者,需要有一个中转站来转发消息。

在异步编程中帮助我们完成更松的解耦, 甚至在 MVC、MVVC 的架构中以及设计模式中也少不了发布-订阅模式的参与。

与其相近的还有观察者模式

场景: 小明取快递

  • 发布订阅模式: 快递小哥 => 快递放再门口超市(还有其他人的快递) => 小明取快递
  • 观察者模式: 快递小哥 => 拿着小明的快递送货上门(只对小明的快递负责) => 小明拿到快递

如图对比:

优点: 在异步编程中进行解耦

缺点: 如果过多的使用发布订阅模式, 维护会成为问题

实现一个发布订阅模式

class EventHub{
  constructor(){
    this.obj = {}
  }
  on(eventType,fn){
    if(!this.obj[eventType]){
      this.obj[eventType] = []
    }
    this.obj[eventType].push(fn)
  }
  emit(eventType,...arguments){
    let arr = this.obj[eventType]
    for (let i = 0; i < arr.length; i++) {
      arr[i].apply(arr[i], arguments)
    }
  }
}

let hub = new EventHub()

hub.on('click', (a) => { // 订阅函数
  console.log(a) // 1
})

hub.emit('click', 1)  

发布早于订阅

我们需要实现这样的逻辑:

var hub = new Event()
hub.emit('click', 1)

hub.on('click', function(a) {
  console.log(a) // 1
})

是利用记忆函数:

class EventHub {
  constructor() {
    this.obj = {}
    this.cacheList = []
  }
  on(eventType, fn) {
    if (!this.obj[eventType]) {
      this.obj[eventType] = []
    }
    this.obj[eventType].push(fn)
    // 先调用缓存队列的方法
    for (let i = 0; i < this.cacheList.length; i++) {
      this.cacheList[i]()
    }
  }
  emit(eventType, ...arguments) {
    const that = this
      cache = () => {
      let arr = that.obj[eventType]
      for (let i = 0; i < arr.length; i++) {
        arr[i].apply(arr[i], arguments)
      }
    }
    // 订阅之前将发布函数都存在缓存队列中
    this.cacheList.push(cache)
  }
}

以上代码实现思路就是把原本在 emit 里触发的函数存到 cacheList, 再转交到 on 中触发。从而实现了发布函数先于订阅函数执行。