🌒

Hi Folks.

设计模式|发布订阅模式

「奇怪?发布订阅模式并没在23种设计模式内?」

事实上发布订阅模式是观察者模式的一种变种,又或是说,发布订阅模式就是观察者模式。当然江湖上这个话题一直争论不休。要说区别,实际上就是发布订阅模式多了一个注册中心而已。


✍🏻定义

所谓定义就是最枯燥,最无聊的东西。诣在把一个东西说的高大上,今天比较懒,太多字了不想写。所以就略过吧,大家可以自己意淫或者等文末看看我有没有心情总结。

「好吧,让GPT写吧」

🙋‍♂️Q: 发布订阅模式是什么,能讲一下定义吗?

🤖GPT:

发布订阅模式(Publish-Subscribe Pattern)是一种消息传递模式,允许发送方(发布者)和接收方(订阅者)之间进行松耦合通信。通过这种模式,发布者不需要知道订阅者的存在,反之亦然。

发布订阅模式通过一个中介(通常称为事件总线或消息代理)来管理消息的发布和订阅。以下是该模式的核心要素:

  1. 发布者(Publisher):发布者将消息发送到中介,而不直接发送给订阅者。发布者只需要知道消息的类型,而不需要知道谁会接收这些消息。
  2. 订阅者(Subscriber):订阅者向中介注册自己感兴趣的消息类型。当相应的消息发布时,中介会通知所有订阅了该消息类型的订阅者。
  3. 中介(Mediator/Message Broker):中介负责管理消息的发布和订阅。它接收来自发布者的消息,并将这些消息传递给所有订阅了该消息的订阅者。

⚽️场景

发布订阅模式和观察者模式在实际场景中应用的很广泛。在常用的框架内也有很多体现。例如Vue中的watchcomputed; 我们常用的各种消息总线,例如: bus总线;Electron的IpcRenderer和IpcMain间的通信模式; 各种状态管理工具……太多了。


⭐️表现形式

在这篇文章中,我想使用Javascript来展示,首先发布订阅模式是一种松耦合的设计模式。通常有两个角色:

  • 发布者(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
42
43
class EventEmitter {
constructor() {
this.events = {};
}

// 订阅事件
on(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
}

// 发布事件
emit(event, ...args) {
if (this.events[event]) {
this.events[event].forEach(listener => {
listener(...args);
});
}
}

// 取消订阅事件
off(event, listener) {
if (this.events[event]) {
this.events[event] = this.events[event]
.filter(l => l !== listener);
}
}
}

// 创建一个事件管理器实例
const eventEmitter = new EventEmitter();

// 订阅事件
eventEmitter.on('message', data => {
console.log('Received message:', data);
});

// 发布事件
eventEmitter.emit('message', 'Hello, world!');
// 输出:
// Received message: Hello, world!

这就是最基本的发布订阅模式结构。再来个实际的例子吧。

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
class Child {
constructor(name) {
this.listeners = []
this.name = name
}

addListener(listener) {
this.listeners.push(listener)
}

// 广播
broadcast(event, msg) {
this.listeners.forEach(listener => {
if (!listener.events[event]) return
listener.events[event](msg)
})
}
}

class Parent {
constructor(name) {
this.name = name
this.events = {}
}
on(event, callback) {
if (!this.events[event]) this.events[event] = callback
}
}

一个孩子和父母的例子,父母就是孩子的订阅者。孩子就是发布者。现在我们让孩子和他的发布者发生关系。

1
2
3
4
5
const child = new Child('子涵');
const dad = new Parent('子涵爸爸');
const mom = new Parent('子涵妈妈');
child.addListener(dad);
child.addListener(mom);

这对父母的孩子很聪明,叫做子涵。一天孩子哭了。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 监听孩子哭了的事件
mom.on('cry', (msg) => {
console.log('别哭了宝贝,妈妈这就来了')
})
dad.on('cry', (msg) => {
console.log('别哭了傻种,爸爸来打你了')
})

child.broadcast('cry', '我好饿')

// output:
// 别哭了宝贝,妈妈这就来了
// 别哭了傻种,爸爸来打你了

于是,在得到父母的关爱后,子涵立马不哭了。这就是一个发布订阅模式的例子。孩子作为事件的发布者,维护了事件中心。父母作为事件的订阅者,监听孩子发布/广播的数据。当然,广播的粒度也可以自定义。


好了,今天的的博客就到这吧……等我下回心血来潮了再来继续写吧。

🫵未完待续;

— 2024年5月16日