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

126 阅读2分钟

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

通常两种模式可看做一样的。都是一种对象间一对多的关系,当一个对象(被观察者/发布者)改变时,会通知所有的(观察者/订阅者)对象。

例1

比如:我们去商店买东西,而商店没货;我们留下联系方式,等老板有货的时候通知我们。 看一个简单的实现:

const Dep = { //发布者仓库
  subs: [],              // 记录订阅者的信息
  addSub(sub) {       // 增加一个订阅者
    if (!this.subs.includes(sub)) {    // 判重
      this.subs.push(sub)
    }
  },
  unSub(sub) {     // 取消订阅
    if (!this.subs.includes(sub)) return;
    const idx = this.subs.indexOf(sub)
    this.subs.splice(idx, 1)
  },
  notify() {      // 发布(将信息发送给订阅者)
    for (const sub of this.subs) {
      sub.update()
    }
  }
}


const customer1 = {
  update() {
    console.log('customer1XX')
  }
}

const customer2 = {
  update() {
    console.log('customer2XX')
  }
}
Dep.addSub(customer2);
Dep.addSub(customer1);
Dep.notify()

例2

再比如:我们去租房子,一般通过中介来匹配房源。中介记录我们的租房需求,比如,带阳台单间、两室套...;房东发布信息给中介,中介等有对应的房源了再通知我们去看房。我们各种各样的租房需求可以看成一个类型。

class Publisher {
  constructor() {
    this.subsMap = {}
  }

  /* 消息订阅 */
  subscribe(type, cb) {
    if (this.subsMap[type]) {
      if (!this.subsMap[type].includes(cb))
        this.subsMap[type].push(cb)
    } else this.subsMap[type] = [cb]
  }

  /* 消息退订 */
  unsubscribe(type, cb) {
    if (!this.subsMap[type] ||
      !this._subsMap[type].includes(cb)) return
    const idx = this.subsMap[type].indexOf(cb)
    this.subsMap[type].splice(idx, 1)
  }

  /* 消息发布 */
  notify(type, ...payload) {
    if (!this.subsMap[type]) return
    this.subsMap[type].forEach(cb => cb(...payload))
  }
}

const publish = new Publisher()

publish.subscribe('type1', message => console.log(message))    // 订阅type1
publish.subscribe('type2', message => console.log(message))

publish.notify('type1', ' type1有了...')   // 通知订阅了type1类型的订阅对象
publish.notify('type2', ' type2有了...') // 通知订阅了type2类型的订阅对象

两者区别

上述两种方式,前者商店和顾客是互相知道的,后者房东和租户是不知道对方的存在通过中介来进行通信。前者为观察者模式,后者是发布订阅模式。 借用网上一张图表示为:

456c4ce343148a20dcc9c6119afd1975.jpg

  • 观察者模式 中的观察者和被观察者之间还存在耦合;
  • 发布 - 订阅模式 中的发布者和订阅者通过消息代理来进行通信,解耦更加彻底

vue中的eventBus和响应式原理都是此模式在实际项目中的应用。

bus的使用

//main
const bus = new Vue()
Vue.prototype.$bus = bus;


//组件1
this.$bus.$on("myEvent", args => { console.log(args) })

//组件2
this.$bus.$emit("myEvent", 'msg')

响应式化大致就是使用 Object.defineProperty 把数据转为 getter/setter,初始化时在getter里添加订阅。在setter时执行发布的过程