【3分钟热度】发布订阅与观察者

205 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

初体验

刚开始接触设计模式时,分不清发布订阅与观察者有啥区别,只知道它们都是解耦对象与对象之间相互依赖的设计模式,懵懵懂懂。

网上搜到的文章有的说这两个没有区别,又的又说有,害,导致自己一些知识体系很混乱,走了很多歪路。

之间的不同

随着码龄变长,脑子里一些模模糊糊的概念通过实践逐渐得出真理。首先可以肯定的说,他们是不一样的,我想很多文章都说过他们的大致概念,它们之间不同的一大地方是:发布订阅是有一个调度中心,按照预设好的key进行发射接收,而观察者模式则是当被观察者状态发生改变后,会通知观察者做出相应动作

、具体到代码层面上来说会更好理解,用vue做例子。

发布订阅

vue的事件注册与触发就是典型的发布订阅模式,通过on订阅事件,emit发布事件,里面有一个中间调度key去找到对应的订阅,从而触发它们的callback

class PublishSubscription {
  constructor () {
    this.deps = {}
  }
  // 订阅
  on (key, callback) {
    let dep = this.deps[key]
    if (!dep) {
      this.deps[key] = dep = []
    }
    dep.push(callback)
  }
  // 发布
  emit (key, ...args) {
    const dep = this.deps[key]
    // 中间调度:试图去找订阅者
    if (Array.isArray(dep)) {
      dep.forEach(callback => callback(...args))
    }
  }
}
const sp = new PublishSubscription
sp.on('key', function (...args) {
  console.log('订阅', ...args)
})
sp.emit('key', 1)

观察者

vue的数据响应式同样是经典的观察者模式,通过Object.defineProperty劫持数据的存取描述符,在get的里收集观察者,set中触发观察者的callback

function vm (obj, key, value) {
  let _value = value
  const deps = []
  Object.defineProperty(obj, key, {
    get () {
        // 收集观察者
        if (window.callback){
          deps.push(window.callback)
          window.callback = null
        }
        return _value
    },
    set (value) {
      // 值一样的话不触发
      if (value === _value) return
      // 触发观察者
      deps.forEach(watch => watch(value, _value))
      _value = value
      return _value
    }
  })
  obj[key]
}
window.callback = function (newValue, oldValue) {
  console.log('观测者', newValue, oldValue)
}
vm(window, 'key', 1)

window.key = 2

image.png

结语

之前看文章,很多时候都云里雾里,但是从代码层面上看,我们可以更具体发现两者的使用方式和实现方式都是不一样的

是否触发callback的判断,就vue来说:

  • 观察者模式:判断新旧值是否一致,不一致才算状态改变,从而遍历观察者callback。
  • 发布订阅模式:只要emit触发到对应的订阅者(即是对应的key),那么就可以触发callback