从下图中可以看出,观察者模式中观察者和目标直接进行交互,而发布订阅模式中统一由调度中心进行处理,订阅者和发布者互不干扰。这样一方面实现了解耦,还有就是可以实现更细粒度的一些控制。比如发布者发布了很多消息,但是不想所有的订阅者都接收到,就可以在调度中心做一些处理,类似于权限控制之类的。还可以做一些节流操作。

1.观察者模式,目标和观察者是基类,目标提供维护观察者的一系列方法,观察者提供更新接口。具体观察者和具体目标继承各自的基类,然后具体观察者把自己注册到具体目标里,在具体目标发生变化时候,调度观察者的更新方法。
比如有个“天气中心”的具体目标A,专门监听天气变化,而有个显示天气的界面的观察者B,B就把自己注册到A里,当A触发天气变化,就调度B的更新方法,并带上自己的上下文。
2.发布订阅,订阅者把自己想订阅的事件注册到调度中心,当该事件触发时候,发布者发布该事件到调度中心(顺带上下文),由调度中心统一调度订阅者注册到调度中心的处理代码。
比如有个界面是实时显示天气,它就订阅天气事件(注册到调度中心,包括处理程序),当天气变化时(定时获取数据),就作为发布者发布天气信息到调度中心,调度中心就调度订阅者的天气处理程序。
1.概述
发布订阅模式又叫观察者模式(Observer Pattern),它是指对象之间一对多的依赖关系,每当那个特定对象改变状态时,所有依赖于它的对象都会得到通知并被自动更新,它是行为型模式的一种。观察者模式内部有一个“主题”对象和若干个“观察者”对象,“主题对象”和“观察者”对象是一对多的依赖关系,当“主题”的状态发生变化时,所有“观察者”都得到通知。本文将详述观察者模式的原理及使用方式。
发布订阅模式的原理基于消息队列或主题,发布者将消息发布到特定的消息队列或主题中,而订阅者可以订阅这些消息队列或主题以接收和处理消息。发布者和订阅者之间的通信是异步的,这意味着发布者发布消息后,订阅者可以在任何时候接收和处理消息。
发布订阅模式的核心思想是将发布者和订阅者解耦,使得它们可以独立地运行和扩展。这种解耦有助于提高系统的灵活性和可伸缩性,因为发布者和订阅者可以根据需要进行扩展和修改,而不会影响彼此的操作。
发布订阅模式在许多领域都有应用,如
- 消息队列
- 事件驱动架构
- 实时数据更新
- 消息推送

它是一种非常有用的通信模式,可以帮助开发人员构建高效、可靠和可扩展的系统。

在发布订阅模式中,发布者将消息发布到消息队列中,而订阅者可以从消息队列中接收和处理消息。消息队列可以作为发布者和订阅者之间的中间件,它可以确保消息的可靠性和有序性。
使用消息队列实现发布订阅模式的步骤如下:
- 创建消息队列:创建一个消息队列来存储和转发消息。
- 发布消息:发布者将消息发布到消息队列中。
- 订阅消息:订阅者订阅消息队列以接收和处理消息。
- 处理消息:订阅者从消息队列中接收消息并进行处理。

在实现发布订阅模式时,需要考虑以下几个方面:
- 消息队列的选择:根据需求选择合适的消息队列,如 RabbitMQ、Kafka 等。
- 消息的格式:定义消息的格式,以便发布者和订阅者能够理解和处理消息。
- 消息的发布和订阅:确定发布者和订阅者如何发布和订阅消息。
- 消息的处理:订阅者需要根据自己的需求处理消息,如数据处理、日志记录等。
- 消息的可靠性:考虑如何确保消息的可靠性,如消息确认、消息重试等。
- 消息的有序性:考虑如何确保消息的有序性,如消息排序、消息分组等。

总之,使用消息队列实现发布订阅模式可以提供高效、可靠和可扩展的通信方式。在实现时,需要根据具体需求进行选择和配置。
发布者负责将消息发布到通信渠道中,而订阅者则负责从通信渠道中接收和处理消息。发布者和订阅者之间的通信是异步的,这意味着发布者发布消息后,订阅者可以在任何时候接收和处理消息。
以下是使用发布订阅模式实现发布者和订阅者的基本步骤:
- 创建通信渠道:创建一个消息队列或主题来存储和转发消息。
- 发布消息:发布者将消息发布到通信渠道中。
- 订阅消息:订阅者订阅通信渠道以接收和处理消息。
- 处理消息:订阅者从通信渠道中接收消息并进行处理。
在实现发布订阅模式时,需要考虑以下几个方面:
- 通信渠道的选择:根据需求选择合适的通信渠道,如消息队列、主题等。
- 消息的格式:定义消息的格式,以便发布者和订阅者能够理解和处理消息。
- 消息的发布和订阅:确定发布者和订阅者如何发布和订阅消息。
- 消息的处理:订阅者需要根据自己的需求处理消息,如数据处理、日志记录等。
- 消息的可靠性:考虑如何确保消息的可靠性,如消息确认、消息重试等。
- 消息的有序性:考虑如何确保消息的有序性,如消息排序、消息分组等。
- 总之,发布订阅模式实现发布者和订阅者之间的通信,提供了一种高效、可靠和可扩展的通信方式。在实现时,需要根据具体需求进行选择和配置。
以下是使用消息主题实现发布订阅模式的基本步骤:
- 创建消息主题:创建一个唯一的消息主题来标识要发布的消息类型。
- 发布消息:发布者将消息发布到特定的消息主题中。
- 订阅消息:订阅者订阅特定的消息主题以接收和处理消息。
- 处理消息:订阅者从订阅的消息主题中接收消息并进行处理。
在实现发布订阅模式时,需要考虑以下几个方面:
- 消息主题的设计:设计合适的消息主题,以便发布者和订阅者能够理解和处理消息。
- 消息的发布和订阅:确定发布者和订阅者如何发布和订阅消息主题。
- 消息的处理:订阅者需要根据自己的需求处理消息,如数据处理、日志记录等。
- 消息的可靠性:考虑如何确保消息的可靠性,如消息确认、消息重试等。
- 消息的有序性:考虑如何确保消息的有序性,如消息排序、消息分组等。
总之,消息主题是发布订阅模式中的重要概念,用于定义发布者和订阅者之间的通信。在实现时,需要根据具体需求进行选择和配置。
1. 最大的区别是调度的地方。
虽然两种模式都存在订阅者和发布者(具体观察者可认为是订阅者、具体目标可认为是发布者),但是观察者模式是由具体目标调度的,而发布/订阅模式是统一由调度中心调的,所以观察者模式的订阅者与发布者之间是存在依赖的,而发布/订阅模式则不会。2. 两种模式都可以用于松散耦合,改进代码管理和潜在的复用。
观察者模式
- // 观察者
- class Observer {
- constructor() {
-
- }
- update(val) {
-
- }
- }
- // 观察者列表
- class ObserverList {
- constructor() {
- this.observerList = []
- }
- add(observer) {
- return this.observerList.push(observer);
- }
- remove(observer) {
- this.observerList = this.observerList.filter(ob => ob !== observer);
- }
- count() {
- return this.observerList.length;
- }
- get(index) {
- return this.observerList(index);
- }
- }
- // 目标
- class Subject {
- constructor() {
- this.observers = new ObserverList();
- }
- addObserver(observer) {
- this.observers.add(observer);
- }
- removeObserver(observer) {
- this.observers.remove(observer);
- }
- notify(...args) {
- let obCount = this.observers.count();
- for (let index = 0; index < obCount; index++) {
- this.observers.get(i).update(...args);
- }
- }
- }
发布/订阅模式
- class PubSub {
- constructor() {
- this.subscribers = {}
- }
- subscribe(type, fn) {
- if (!Object.prototype.hasOwnProperty.call(this.subscribers, type)) {
- this.subscribers[type] = [];
- }
-
- this.subscribers[type].push(fn);
- }
- unsubscribe(type, fn) {
- let listeners = this.subscribers[type];
- if (!listeners || !listeners.length) return;
- this.subscribers[type] = listeners.filter(v => v !== fn);
- }
- publish(type, ...args) {
- let listeners = this.subscribers[type];
- if (!listeners || !listeners.length) return;
- listeners.forEach(fn => fn(...args));
- }
- }
-
- let ob = new PubSub();
- ob.subscribe('add', (val) => console.log(val));
- ob.publish('add', 1);