JavaScript - 观察者与发布订阅者模式

观察者模式

  • 观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,其所有依赖者都会收到通知并自动更新。
  • 当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。
  • 可以看作拍卖场景,拍卖师观察最高标价,然后通知给其他竞价者竞价。
优点:
  • 观察者和被观察者是抽象耦合的,实现简单,主题和观察者之间的关系明确,适用于一对多的关系。
缺点:
  • 当观察者较多时,主题的通知机制可能会导致性能问题,因为每个观察者都会被直接通知。

具体实现

观察者
javascript
12345678
class Observer {
constructor(name) {
this.name = name
}
update(data) {
console.log(`观察者${this.name} 收到了: ${data}`)
}
}
被观察者
javascript
1234567891011121314
class Subject {
constructor() {
this.observers = []
}
add(observer) {
this.observers.push(observer)
}
notify(data) {
this.observers.forEach((i) => i.update(data))
}
delete(observer) {
this.observers = this.observers.filter((i) => i !== observer)
}
}

具体使用

一个栗子
javascript
123456789101112131415
const sub = new Subject()
const observer1 = new Observer('A')
const observer2 = new Observer('B')
sub.add(observer1)
sub.add(observer2)
sub.notify('一条新闻')
// 观察者A 收到了: 一条新闻
// 观察者B 收到了: 一条新闻
sub.notify('今天的天气是晴天')
// 观察者A 收到了: 今天的天气是晴天
// 观察者B 收到了: 今天的天气是晴天
sub.delete(observer1)

发布订阅者模式

  • 发布/订阅(Publish–subscribe pattern)是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。
  • 而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在。同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者(如果有的话)存在。
  • 可以看作一个为邮件系统,你可以作为订阅者订阅某个网站的通知,邮件系统在其中充当发布订阅中心的角色,而发布者则是你订阅的网站。
优点:
  • 解耦发布者和订阅者,使得系统更灵活,扩展性更好;支持多对多的关系,可以实现更复杂的消息通信。
缺点:
  • 引入了调度中心,可能会增加系统的复杂度;由于间接通信,调试和理解代码可能会稍微困难一些。

具体实现

调度中心
javascript
12345678910111213141516171819202122232425262728
class Dep {
constructor() {
this.subscriptions = {}
}
subscribe(topic, callback) {
if (!this.subscriptions[topic]) {
this.subscriptions[topic] = []
}
this.subscriptions[topic].push(callback)
}
unSubscribe(topic) {
if (!this.subscriptions[topic]) {
return
}
delete this.subscriptions[topic]
}
publish(topic, data) {
if (!this.subscriptions[topic]) {
return
}
this.subscriptions[topic].forEach((callback) => {
callback(data)
})
}
}
发布者
javascript
123456789
class Publisher {
constructor(pub) {
this.pub = pub
}
publishMessage(topic, message) {
this.pub.publish(topic, message)
}
}
订阅者
javascript
123456789101112131415161718
class Subscriber {
constructor(sub, name) {
this.sub = sub
this.name = name
}
subscribeToTopic(topic) {
this.sub.subscribe(topic, (data) => {
console.log(`订阅者 ${this.name} 收到${topic}消息: ${data}`)
})
}
unSubscribeToTopic(topic) {
this.sub.unSubscribe(topic, (data) => {
console.log(`订阅者 ${this.name} 取消订阅${topic}消息: ${data}`)
})
}
}

具体使用

一个栗子🌰
javascript
12345678910111213141516
const dep = new Dep()
const publisher = new Publisher(dep)
const subscriber1 = new Subscriber(dep, 'A')
const subscriber2 = new Subscriber(dep, 'B')
subscriber1.subscribeToTopic('news')
subscriber2.subscribeToTopic('weather')
publisher.publishMessage('news', '一条新闻')
// 订阅者 A 收到news消息: 一条新闻
publisher.publishMessage('weather', '今天的天气是晴天')
// 订阅者 B 收到weather消息: 今天的天气是晴天
subscriber1.unSubscribeToTopic('news')