前言
从第一篇工厂模式开始,我会持续地更新每一种设计模式的内容,争取用通俗易懂的语言讲解和解释清楚。如果对你学习设计模式有帮助,请不要吝啬手中的赞~ 如果对文章内容有任何疑惑都可以在评论区提出和讨论~
本系列文章中的完整源码已上传 github 仓库,你可以在这里 github.com/FatMii/Desi...获取。
同样的,如果对你有帮助,请给我一个star~谢谢
设计模式合集链接:
Hello~大家好,前面四篇我们已经学习工厂模式
,单例模式
,责任链模式
,观察者模式
,观察者模式
。本篇我们继续学习第五种设计模式:发布订阅模式
。
发布订阅模式
与观察者模式
在很多方面确实非常相似,这也是为什么许多开发者很容易混淆这两种模式的原因。两者都是用于在不同组件间实现数据和事件通信的设计模式,但它们在实现细节和使用场景上有着关键的差异。在深入探讨发布订阅模式之前,让我们先回顾一下在上一篇文章中提到的观察者模式的一些缺点。
一、回顾观察者模式实现
javascript
// 应用场景:1.dom监听 2.降价商品
class Subject {
constructor() {
this.Observers = [];
}
add(observer) {
this.Observers.push(observer);
}
remove(observer) {
this.Observers.filter((item) => item === observer);
}
notify() {
this.Observers.forEach((item) => {
item.update();
});
}
}
//定义观察者对象
class Observer {
constructor(name) {
this.name = name;
}
update() {
console.log(`my name is:${this.name}`);
}
}
let sub = new Subject();
let obs1 = new Observer("observer11");
let obs2 = new Observer("observer22");
sub.add(obs1);
sub.add(obs2);
sub.notify();
二、观察者模式的缺点
缺点一: 无法对事件通知进行细分管控
想象这样一个场景:你是一名专注的程序员,正沉浸在代码中,试图解决一个复杂的问题。突然,"叮咚",你的电脑弹出一个通知:立即召开的公司全体大会。你扫了一眼会议的议题,却发现这次会议与你的工作完全无关。尽管如此,你不得不中断手头的工作,前往会议室。
这就是我们所说的"大喇叭通知法"在作祟。在这种机制下,公司的领导(即老板(Subject)
)一声令下,所有的员工(即每个人(Observer)
),无论是需求分析师、开发人员还是测试工程师,都必须响应这一通知。即便今天的会议内容仅与需求组相关,你这位忙碌的开发人员也不得不放下手中的代码,参加一个与你职责不相关的会议。
这种通知方式不仅效率低下,而且极大地浪费了员工的时间和公司的资源,特别是当涉及到那些与会议主题毫无关联的人员时。它反映出一个问题:通知系统的设计缺乏针对性和细致的分层管理,使得每次广播都无差别地覆盖所有人员,而不考虑信息的相关性或重要性。这不仅打扰了员工的正常工作流程,也可能对工作满意度和整体生产效率产生负面影响。
缺点二:观察者与主题耦合性太高
在观察者模式中,主题
与观察者
之间的关系是紧密且直接的。例如,在一个公司环境里,主题(如公司的管理层)
直接管理着所有的观察者(即员工
)。这意味着每位员工都需要直接向管理层注册,并保持对其所有指令的警觉。
这种设计虽然确保了信息的快速传递,但同时也带来了一定的弊端。由于观察者需要直接与主题连接,这就增加了双方的依赖性
,使得系统的灵活性降低。一旦需要调整或更新系统,每个组件之间的密切关系可能会导致复杂的连锁反应,进而增加管理和维护的难度。例如,简单的调整如增加一个团队成员或改变一个工作流程,都可能需要考虑到对整体系统的潜在影响。
此外,主题需要持续维护和更新
其观察者名单,确保在任何关键信息更新时,所有相关人员都能接收到通知。这种管理方式在保持组织结构和信息流动的同时,也可能导致效率低下和资源浪费。
三、发布订阅模式代码实现
我们话不多说,直接上代码看看发布订阅模式和如何解决这个问题的
javascript
class PubSub {
constructor() {
this.subscribers = {};
}
// 订阅方法
subscribe(event, callback) {
// 如果没有该事件的订阅者数组,则初始化一个空数组
if (!this.subscribers[event]) {
this.subscribers[event] = [];
}
// 将回调函数添加到订阅者数组
this.subscribers[event].push(callback);
// 返回一个取消订阅的函数
return () => this.unsubscribe(event, callback);
}
// 发布方法
publish(event, data) {
// 如果有订阅者,则调用每个回调函数,传入数据
if (this.subscribers[event]) {
this.subscribers[event].forEach(callback => callback(data));
}
}
// 取消订阅方法
unsubscribe(event, callback) {
if (this.subscribers[event]) {
// 过滤掉需要取消的回调函数
this.subscribers[event] = this.subscribers[event].filter(cb => cb !== callback);
}
}
}
// 使用例子
const pubsub = new PubSub();
// 订阅事件
const subscription = pubsub.subscribe('message', data => console.log(`Received message: ${data}`));
// 发布事件
pubsub.publish('message', 'Hello world!');
// 取消订阅
subscription(); // 或者 pubsub.unsubscribe('message', callback)
细分事件
在发布订阅模式中,我们使用一个 subscribers
对象来存储事件名和对应的事件处理方法。这种结构与观察者模式中的简单数组相比,提供了更多的灵活性和精确控制。具体来说,subscribers
对象将事件名称映射到一个数组,这个数组包含了所有订阅该事件的方法,如下所示:
javascript
{
'aEvent':[method1,method2],
'bEvent':[method3,method4],
'cEvent':[method5,method6]
}
在这种结构下,当 aEvent
事件被触发时,只有 method1
和 method2
会被调用,而其他订阅的处理方法不会受到干扰,从而避免了不必要的操作和资源浪费。这种方法的分离和组织极大提高了系统的效率和响应能力。
新增中间层实现解耦
进一步地,发布订阅模式引入了一个重要的中间层---调度中心
,这个中心承担着管理和调度所有事件通知的责任。这一层相当于一个信息交换枢纽,确保了信息从发布者
到订阅者
的有效传递。与观察者模式
直接的观察和通知相比,发布订阅模式
通过调度中心实现了发布者和订阅者之间的解耦。
总结
发布订阅模式的特点
发布订阅模式作为一种广泛应用于事件驱动架构中的设计模式,具备以下显著特点:
- 解耦能力强 :发布者和订阅者之间无需直接通信,他们通过一个
中介(调度中心)
来交互,这减少了组件间的依赖关系。 - 灵活性高:支持动态地添加或移除订阅者,系统可以在运行时适应变化,方便地扩展或修改功能。
- 扩展性好:适用于大规模分布式系统,能够有效地处理大量的事件和消息,支持多个订阅者同时监听同一事件。
缺点
尽管发布订阅模式提供了许多优势,但它也有一些潜在的缺点:
- 系统复杂性:引入调度中心增加了系统的复杂性,需要额外的维护和管理,可能会增加系统的开发和运行成本。
- 资源消耗:维护大量的订阅关系和消息传递可能会消耗较多的计算和存储资源。
结语
发布订阅模式就像是公司里的智能通知系统,它确保只有相关的部门和人员会收到特定会议的通知。这种模式让整个公司的信息流通变得更加高效,因为它避免了不必要的干扰,让每个人都可以专注于对他们真正重要的信息。
想象一下,如果每次公司有个部门会议,无论相关与否,所有员工都收到通知,那会多烦人!但有了发布订阅模式,开发组就不会被不断打扰,去参加只与营销团队相关的策略讨论。这不仅节省了大家的时间,也提高了工作效率。