差异
在观察者模式中,观察者需要直接订阅目标事件。在目标发出内容改变的事件后,直接接收事件并作出响应。发布订阅模式相比观察者模式多了个事件通道,订阅者和发布者不是直接关联的。
比如你去外面吃饭,就经常出现两种模式
1.观察者模式:你进入餐馆,坐在餐馆里等待叫号,当叫号时,你就知道又有人可以吃饭了,你会下意识的看看自己的号。
这个过程就是观察者模式。餐馆是观察的对象,餐馆的座位是观察者队列,叫号时通知观察者(顾客),看号码就是执行相应的函数。
发布订阅模式:你取了一个号,但还想去其他地方逛逛,然后你用微信扫了号码,就走了,当叫号时,微信会推送消息给你,这个时候你就会看看自己是否到号了。
取了一个号扫码就是加入了一个消息队列,叫号的时候就能推送给你,微信就是这个中间队列。当餐馆叫号下一个的时候(发布),这个时候扫码过的人就都知道了,然后会查看自己(观察者)的号码(更新)。
差别也就显而易见了,人(观察者所处的位置在哪,是否有通道(微信))
const public = (() => {
var topics = {};
function join (topic, fn) {
if(!topics[topic]){
topics[topic] = []
}
topics[topic].push(fn)
}
function publish (topic, ...args) {
if(!topics[topic])
return;
for(let fn of topics[topic]) {
fn(...args)
}
}
return {
join,
publish
}
})()
public.join('test',function(a,b){ //订阅者A订阅了test事件
console.log(a,b);
});
public.publish('test','123','HH'); //123 HH(发布者B发布了test事件)
上面的例子是发布订阅模式。订阅者A和发布者B是通过pubsub这个对象关联起来的,他们没有直接的交流。
那么真正的观察者模式是怎么样的?
一个或多个观察者对目标的状态感兴趣,通过将自己依附在目标对象上以便注册所感兴趣的内容。目标状态发生改变并且观察者可能对这些改变感兴趣,会发送一个通知消息,调用每个观察者的更新方法。当观察者不再对目标状态感兴趣时,他们可以简单将自己从中分离。
/*
* 观察者模式
*/
class Subject {
constructor() {
this.subs = [];
}
addSub(sub){
this.subs.push(sub)
}
notify() {
this.subs.forEach(sub=> {
sub.update()
})
}
}
class Observer {
update () {
console.log('update')
}
}
let subject = new Subject();
let ob = new Observer();
//目标添加观察者了
subject.addSub(ob);
//目标发布消息调用观察者的更新方法了
subject.notify(); //update
优劣
个人觉得发布/订阅模式比较简单,使用的也比较广泛。由于他订阅者和发布者不直接关联的特点我们完全可以把管理事件的对象写到一个单独文件中,作为库来使用。发布订阅模式中,双方不知道对方的存在,而观察者模式中,目标和观察者是直接联系起来的。具体选择什么模式,得视场景而定。一般来说,发布/订阅就够用了,简单清晰,符合我们dom事件编程的观念。