实现一个简单的发布订阅模式

一、发布订阅模式是什么?

发布订阅模式是对象间的一种一对多的关系,由发布者、订阅者、消息管理器三部分组成,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知。

不论是在程序世界里还是现实生活中,发布—订阅模式的应用都非常广泛。我们先看一下现实中的例子: 小明最近看上了一套房子,到了售楼处之后才被告知,该楼盘的房子早已售罄。好在售楼MM告诉小明,不久后还有一些尾盘推出。开发商正在办理相关手续,手续办好后便可以购买。但到底是什么时候,目前还没有人能够知道。 于是小明记下了售楼处的电话,以后每天都会打电话过去询问是不是已经到了购买时间。除了小明,还有小红,小强,小龙也会每天向售楼处咨询这个问题。一个星期过后,售楼MM决定辞职,因为厌倦了每天回答1000个相同的电话。 当然现实中没有这么笨的销售公司,实际上故事是这样的:小明离开之前,把电话号码留在售楼处。售楼MM答应他,新楼盘一推出就马上发信息通知小明。小红、小强、小龙也是一样,他们的电话号码都被记在售楼处的花名册上,新楼盘推出的时候,售楼MM会翻开花名册,遍历上面的电话号码,依次发送一条短信通知他们。

二、发布订阅模式的优缺点

优点:

缺点:

    创建订阅者需要消耗一定的时间和内存 虽然可以弱化对象之间的联系,但是如果过度使用的话,代码反而不太好理解和维护。特别是有多个发布者和订阅者的嵌套到一起的时候,要跟踪一个bug并不是一件轻松的事情

三、实现一个通用的发布订阅模式

// 定义事件中心类
class MyEvent {
          
   
  handlers = {
          
   } // 存放事件 map,发布者,存放订阅者
// 绑定事件
  $on(type, fn) {
          
   
    if (!Reflect.has(this.handlers, type)) {
          
    // 如果没有定义过该事件,初始化该订阅者列表
      this.handlers[type] = []
    }
    this.handlers[type].push(fn) // 存放订阅的消息
  }

// 触发事件
  $emit(type, ...params) {
          
   
    if (!Reflect.has(this.handlers, type)) {
          
    // 如果没有该事件,抛出错误
      throw new Error(`未注册该事件${
            
     type}`)
    }
    this.handlers[type].forEach((fn) => {
          
    // 循环事件列表,执行每一个事件,相当于向订阅者发送消息
      fn(...params)
    })
  }
// 取消订阅
  $remove(type, fn) {
          
   
    if (!Reflect.has(this.handlers, type)) {
          
   
      throw new Error(`无效事件${
            
     type}`)
    }
    if (!fn) {
          
    // 如果没有传入方法,表示需要将该该类型的所有消息取消订阅
      return Reflect.deleteProperty(this.handlers, type)
    } else {
          
   
      const inx = this.handlers[type].findIndex((handler) => handler === fn)
      if (inx === -1) {
          
    // 如果该事件不在事件列表中,则抛出错误
        throw new Error(无效事件)
      }
      this.handlers[type].splice(inx, 1) // 从事件列表中删除该事件
      if (!this.handlers[type].length) {
          
    // 如果该类事件列表中没有事件了,则删除该类事件
        return Reflect.deleteProperty(this.handlers, type)
      }
    }
  }
}
经验分享 程序员 微信小程序 职场和发展