最近看到很多文章分析发布订阅模式和观察者模式的异同,其实在大多关于设计模式的书籍里面都认为二者是一样的。
发布订阅模式,又叫做观察者模式,又叫做消息机制,它定义对象间的一种一对多的依赖关系,当一个对象状态改变的时候,所有依赖于它对象都将得到通知.
个人感觉他们俩也是一样的,只不过是写法不同,理解的切入角度不同造成他们好像是两种设计模式。
- 最简单的发布订阅模式
const eventCenter = {
// 调度中心
list: [],
// 订阅
on(fn){
this.list.push(fn)
},
// 发布
emit(){
this.list.forEach(fn => {
fn()
});
}
}
eventCenter.on(()=>{console.log('A订阅了')})
eventCenter.on(()=>{console.log('B订阅了')})
eventCenter.emit() // A订阅了 B订阅了
把调用eventCenter.on的对象叫做订阅者
把调用eventCenter.emit的对象叫做发布者
订阅者和发布者通过一个eventCenter完成了解耦
- 带着面向对象的思维来写
// 定义被观察者
function BeObserve() {
this.observeList = []
this.add = function(observe){
this.observeList.push(observe)
}
this.notify = function(){
this.observeList.forEach((observe)=>{
observe.update()
})
}
}
// 定义观察者
function Observe(change) {
this.update = function(){
console.log(change)
}
}
const beObserve = new BeObserve()
const observe1 = new Observe('界面温度+1')
const observe2 = new Observe('界面湿度+11')
const observe3 = new Observe('风力增加+11')
beObserve.add(observe1)
beObserve.add(observe2)
beObserve.add(observe3)
beObserve.notify()
beObserve称作被观察者
observe1,observe2,observe3称作观察者
二者对比
对比1中来看可以把observe1,observe2,observe3叫做订阅者
把调用beObserve.notify()的对象叫做 发布者。原理都是,订阅者通过调用on/add完成订阅,发布者通过调用emit/notify通知所有订阅者。订阅者和发布者通过eventCenter/beObserve松耦合。
总结
将调用调用on/add的称作订阅者;将调用emit/notify称作发布者;这么来看二者就完全相同了
最后来张流传慎广的差异图(修订版)
实现一个通用的发布订阅模式
const eventCenter = (function(){
let eventList = {}, on, emit, off, once;
on = function(type, fn){
if (!eventList[type]) {
eventList[type] = []
}
eventList[type].push(fn)
}
emit = function(){
const [type, ...args] = arguments
const fns = eventList[type]
if (!fns || fns.length === 0) {
return false
}
fns.forEach(fn => {
fn.apply(this, args)
});
}
off = function(type, fn){
const fns = eventList[type]
if (!fns || fns.length === 0) {
return false
}
eventList[type] = fns.filter((item)=>{
console.log(item[fn], 'item')
console.log(fn, 'fn')
console.log(fn !== item.fn, 'boolean')
// fn !== item[fn] hack once订阅的事件不触发不能取消订阅BUG
return fn !== item && fn !== item.fn
})
console.log(eventList[type].toString(), 'eventList[type]')
}
once = function(type, fn){
function one(){
off(type, one)
fn.apply(this, arguments)
}
one.fn = fn // hack once订阅的事件不触发不能取消订阅BUG
on(type, one)
}
return {on,emit,off,once}
})()