观察者模式-发布订阅者模式的不同

在之前的学习过程中,一直对观察者模式、发布-订阅者模式的概念模棱两可,今天找到了机会好好的补习了一下。
首先,什么是观察者模式?什么是发布-订阅者模式呢?

观察者模式中主体(Subject)和观察者(Observer)是互相感知的
发布(Publisher)-订阅者(Subscriber)模式是借助第三方来实现调度的,发布者(Publisher)和订阅者(Subscriber)之间互不感知

这里对概念进行了了解,但是其具体的实现究竟是个什么样的呢?看如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
const pubSub = (() => {
const eventChannelTopics = {};

function subscribe(topic, fn) {
if (!eventChannelTopics[topic]) {
// 当前订阅topic不存在则创建一个对应的topic数组
eventChannelTopics[topic] = [];
}
// 将当前订阅topic存入对应的topic数组
eventChannelTopics[topic].push(fn);
}

function publish(topic, ...args) {
// 没订阅的topic在进行发布时直接忽略
if (!eventChannelTopics[topic]) return false;

// 发布当前topic时,把对应的(eventChannelTopics[topic])topic数组
// 中的订阅topic依次全部执行
for (let fn of eventChannelTopics[topic]) {
fn(...args);
}
}

return {
subscribe,
publish
}
})();

// 订阅者 A
pubSub.subscribe('test', function(a, b) {
console.log('这里是第一个subscribe订阅了test事件', a, b);
});
// 订阅者 B
pubSub.subscribe('test', function(a, b) {
console.log('这里是第二个subscribe订阅了test事件', a, b);
});
// 这两个订阅的 test 事件在 publish 执行后会依次将订阅事件全部执行

// 发布者 P
pubSub.publish('test', '12', 'hh');

这里实际上就是把存放到数组中的所有函数全部执行了一遍,但是对于直接使用咩有亲自实现过代码的开发者来说,这确实是比较神奇的,js居然能订阅发布消息。
有时候我们会发现,有人叫它发布-订阅者模式,又有人叫它观察者模式,总觉得这两个叫法都对,但是,它们两者(观察者模式、发布-订阅者模式)真的不一样。

区别

在观察者模式中,观察者(Observer)需要直接订阅目标事件。在目标(Subject)发出内容改变事件后,直接接收事件并作出响应。
发布-订阅者模式,相比观察者模式多了个事件通道,订阅者(Subscriber)和发布者(Publisher)不是直接关联的。
从这里的描述就可以看出前面的第一个例子是发布-订阅模式。代码中的订阅者A、订阅者B和发布者P都是通过pubSub这个对象关联起来,在发布和订阅之间咩有直接的交流。

即真正的观察者模式是怎样的?

一个或者多个观察者(Observer)对目标(Subject)的状态感兴趣,通过将自己依附在目标对象上以便注册所有感兴趣的内容。目标状态发生改变并且观察者可能对这些改变感兴趣,即发送一个通知消息,调用每个观察者的更新方法。当观察者不再对目标状态感兴趣时,它们可以将自己从中分离。

如下观察者模式代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 定义主体
class Subject {
constructor() {
this.subsArr = [];
}

addSub(sub) {
this.subsArr.push(sub);
}

notify() {
// 将已经添加的事件全部执行
this.subsArr.forEach(sub => {
sub.update();
});
}
}
// 定义观察者
class Observer {
update() {
console.log('observer update');
}
}

// 事件触发
const subject = new Subject();
const observer = new Observer();

// 目标添加观察事件
subject.addSub(observer);
// 目标发布消息,调用观察者的更新方法
subject.notify();

可以看出这里的目标(Subject)和观察者(Observer)是直接联系在一起的,观察者把自己添加到了目标对象中;这里可以看出它和发布-订阅模式差别还是很大的。在这种模式下,目标(Subject)更像一个发布者,它让添加进来的所有观察者(Observer)都执行了update函数,而观察者就像一个订阅者。

总结

发布-订阅模式中发布者和订阅者两者不直接关联,双方都不知道对方的存在,而是通过第三方进行事件管理;而观察者模式是直接将目标和观察者联系起来。
两者在实际使用中根据实际情况选择即可。