• home > theory > engineering > model >

    观察者模式与发布订阅模式的区别

    Author:zhoulujun Date:

    观察者模式和发布-订阅模式在细节和实现上,这两种模式有着明显的不同。观察者模式中主体和观察者是互相感知的,发布-订阅模式是借助第三方来实现调度的,发布者和订阅者之间互不感知

    观察者模式和发布-订阅模式在细节和实现上,这两种模式有着明显的不同。

    观察者模式(Observer Pattern)

    在观察者模式中,一个或多个观察者对主题(Subject)的状态感兴趣,并在主题状态改变时接收通知。

    +------------+     +------------+

    |  观察者    |<--->|  目标      |

    +------------+     +------------+

    观察者需要显式地注册(订阅)到主题上,以便当主题状态发生变化时,主题直接调用观察者提供的方法来通知它们。这种模式通常在观察者(Observers)和被观察目标(Subject)之间建立较紧密的耦合关系。

    • 主题(Subject)直接持有观察者(Observer)的引用。

    • 对象之间的通信是直接进行的。

    发布订阅模式,微商类比

    观察者模式面向事件驱动编程(观察者模式)




    发布-订阅模式(Publish-Subscribe Pattern)

    发布-订阅模式在观察者模式的基础上引入了一个消息代理(也称为事件通道或消息队列)的概念,以实现发布者和订阅者之间的解耦。

    +------------+     +------------+     +------------+

    |  观察者    |<--->|  事件调度中心  |<--->|  目标      |

    +------------+     +------------+     +------------+

    在这个模式中,发布者将事件发布到一个中间的消息通道,而订阅者从该消息通道订阅事件。这样,发布者和订阅者不需要了解对方的存在,只与消息通道进行交互,从而实现了更高的系统解耦。

    • 发布者和订阅者之间通过消息代理进行间接通信。

    • 提高了系统各部分的独立性和可替换性。

    设计模式之发布-订阅模式图一 发布-订阅模式

    发布-订阅好比观察者模式增加了一个中介者,发布者和订阅着只和中介者打交到。中介者持有发布者和订阅者都引用或传入的回调,用来做订阅和通知。

    观察者模式与发布-订阅模式在事件驱动方式中的应用

    观察者模式

    • 直接通信:主题(Subject)直接管理一组观察者(Observer),并在状态发生变化时直接通知它们。这意味着观察者需要直接注册(或订阅)到主题对象上。

    • 紧耦合:观察者必须实现一个特定的接口(这里是update方法),以便主题能够通过这个接口通知到每个观察者。这导致观察者与主题之间的耦合度相对较高,因为观察者必须知道主题并按照主题的要求实现具体接口。

    • 实现简单:由于观察者直接注册到主题上,实现这一模式相对直接和简单,适用于对象间关系较为固定,通信路径明确的场景。

    发布-订阅模式

    • 间接通信:介于发布者(Publisher)和订阅者(Subscriber)之间的通信是通过一个中间件(在上述示例中为PubSub类)来进行的,该中间件维护了事件(或主题)及其相应回调函数的映射。这个机制使得发布者和订阅者之间的关联变得更加灵活和松散。

    • 低耦合:订阅者不需要知道谁是发布者,它只需要知道从哪里订阅感兴趣的事件。同样,发布者不需要知道谁订阅了事件,它只需要将事件发布到中间件。中间件负责将事件通知给所有相关的订阅者。

    • 复杂实现:由于加入了中间件的概念,实现发布-订阅模式相比观察者模式在逻辑上稍微复杂一些。但是这种复杂性带来的好处是更高的灵活性和低耦合度,适用于大型应用和分布式系统。

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

    观察者模式中主体和观察者是互相感知的

    • 应用场景需要较紧密的对象之间互动,并且这些对象之间的关系比较固定,那么观察者模式可能是较好的选择。

    • 系统需要高度的解耦,以及支持大规模的、分布式的交互,那么发布-订阅模式可能更加合适,因为它通过消息代理来减少系统组件之间的直接依赖。总的来说,发布-订阅模式适合更复杂的场景。

      • 在「一对多」的场景下,发布者的某次更新只想通知它的部分订阅者?

      • 在「多对一」或者「多对多」场景下。一个订阅者依赖于多个发布者,某个发布者更新后是否需要通知订阅者?还是等所有发布者都更新完毕再通知订阅者?


    观察者模式和订阅-发布模式代码实现

    观察者模式

    class Subject {
      constructor() {
        // 主题(Subject)直接管理一组观察者(Observer)
        this.observers = []; // 观察者列表
      }
    
      subscribe(observer) {
        this.observers.push(observer);
      }
    
      unsubscribe(observer) {
        this.observers = this.observers.filter(obs => obs !== observer);
      }
    
      notify(data) {
        // 在状态发生变化时直接通知它们。这意味着观察者需要直接注册(或订阅)到主题对象上。
        this.observers.forEach(observer => observer.update(data));
      }
    }
    
    class Observer {
      constructor(name) {
        this.name = name;
      }
      //观察者必须实现一个特定的接口(这里是update方法),以便主题能够通过这个接口通知到每个观察者。这导致观察者与主题之间的耦合度相对较高,因为观察者必须知道主题并按照主题的要求实现具体接口。
      update(data) {
        console.log(`${this.name} received data: ${data}`);
      }
    }
    
    // 使用
    const subject = new Subject();
    const observer1 = new Observer("Observer 1");
    const observer2 = new Observer("Observer 2");
    
    subject.subscribe(observer1);
    subject.subscribe(observer2);
    
    subject.notify("Hello World!");

    上面的代码定义了一个主题(Subject)类和一个观察者(Observer)类。主题类管理一组观察者并提供通知方法,观察者类则具有更新方法以响应通知。

    发布-订阅模式

    发布-订阅模式引入了一个中间层(通常被称为“事件通道”或“Broker”),这使得发布者和订阅者之间的联系更加松散。

    class PubSub {
        constructor() {
            this.subscribers = {};
        }
    
        subscribe(event, callback) {
            if (!this.subscribers[event]) {
                this.subscribers[event] = [];
            }
            this.subscribers[event].push(callback);
        }
    
        unsubscribe(event, callback) {
            this.subscribers[event] = this.subscribers[event].filter(cb => cb !== callback);
        }
    
        publish(event, data) {
            if (this.subscribers[event]) {
                this.subscribers[event].forEach(callback => callback(data));
            }
        }
    }
    
    // 使用
    const pubSub = new PubSub();
    
    const callback1 = data => console.log(`Callback 1 received data: ${data}`);
    const callback2 = data => console.log(`Callback 2 received data: ${data}`);
    
    pubSub.subscribe('anEvent', callback1);
    pubSub.subscribe('anEvent', callback2);
    
    pubSub.publish('anEvent', 'Hello world!');

    最典型的就是 Vue事件总线(Event Bus),具体参看原生js实现事件监听类on/emit/off方法,Vue event事件机制解读


    最后,题外话:

    观察者模式(Observer Pattern)属于23种设计模式之一。它是一种对象行为模式,用于定义对象间的一种一对多的依赖关系。

    发布订阅模式(Publish-Subscribe Pattern)虽然与观察者模式有一定的相似性,但它并不是23种设计模式之一。发布订阅模式是一种消息传递模式,用于解耦发布者和订阅者。在发布订阅模式中,发布者发布消息,订阅者订阅消息,然后代理服务器负责将消息分发给订阅者。这种模式适用于实现事件处理系统,但并不限定于观察者模式



    转载本站文章《观察者模式与发布订阅模式的区别》,
    请注明出处:https://www.zhoulujun.cn/html/theory/engineering/model/9072.html