持续创作,加速成长!这是我参与「掘金日新计划 · 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
结语
之前看文章,很多时候都云里雾里,但是从代码层面上看,我们可以更具体发现两者的使用方式和实现方式都是不一样的
是否触发callback的判断,就vue来说:
- 观察者模式:判断新旧值是否一致,不一致才算状态改变,从而遍历观察者callback。
- 发布订阅模式:只要emit触发到对应的订阅者(即是对应的key),那么就可以触发callback