likes
comments
collection
share

观察者模式, 发布-订阅, 事件总线 傻傻分不清?先有问题再有答案 如何理解观察者模式? 有什么使用场景? 发布订阅模式

作者站长头像
站长
· 阅读数 17

先有问题再有答案

  1. 如何理解观察者模式?
  2. 有什么使用场景?
  3. 发布订阅模式是什么?有什么用?
  4. 观察者, 发布订阅, 事件总线 三者有什么关系?
  5. 三者有什么相同点和不同点?

观察者模式

观察者模式, 发布-订阅, 事件总线 傻傻分不清?先有问题再有答案 如何理解观察者模式? 有什么使用场景? 发布订阅模式 观察者模式是一种 一对多的关系:有一个主体,拥有多个观察者。主体状态发生改变,所有的观察者都会被通知。 主体和观察者之间是直接关系

demo

class Subject {
  constructor() {
    this.observers = [];
  }

  addObserver(observer) {
    this.observers.push(observer);
  }

  removeObserver(observer) {
    const index = this.observers.findIndex(obs => obs === observer);
    this.observers.splice(index, 1);
  }

  notify(data) {
    this.observers.forEach(observer => observer.update(data));
  }
}

class Observer {
  constructor()  update(data) {
    console.log('Observer notified', data);
  }
}

const observer1 = new Observer();
const observer2 = new Observer();
const subject = new Subject();

subject.addObserver(observer1);
subject.addObserver(observer2);

subject.notify('Some data');  //所有观察者都会接收到 "Some data"

场景

适用于一个对象的状态变化需要通知多个其他对象,并且这些对象需要实时响应这些变化的场景。

例如,在Vue中,每个组件实例都有对应的观察者实例(Observer),这个观察者会在组件初始化的过程中将组件的数据变为响应式。在这个过程中,Vue会遍历这些数据,并将它们转化为getter/setter。这样,每次访问或修改这些数据时就会触发getter/setter。

当这些数据被用在视图上时,渲染函数将会被转化为一个"观察者",即Watcher实例。每个依赖于某个数据的Watcher都将被添加到这个数据对应的Dep列表中。

当数据变化时,setter就会被触发,并通知对应的Dep实例。Dep实例则通知它所管理的所有Watcher实例,最后这些Watcher就会响应这个变化,例如更新视图。

所以,Vue的响应式系统就是通过观察者模式实现的。“数据”可以被看做是“主题”,“Watcher”可以被看做是“观察者”。当数据(主题)发生变化时,所有依赖于它的Watcher(观察者)都会被通知。

这就是Vue在依赖收集时使用观察者模式来记录数据与副作用函数的关系,从而在数据变化后可以立即找到并执行相应的副作用函数。

发布订阅模式

适用于需要实现事件的广播和订阅机制的场景,其中事件的 发布者和订阅者之间不需要直接通信,而是通过一个中介(如消息队列或事件总线)来进行通信

demo

class Publisher {
    constructor() {
        this.subscribers = {};
    }

    subscribe(event, callback) {
        if (!this.subscribers[event]) {
            this.subscribers[event] = [];
        }
        this.subscribers[event].push(callback);
    }

    unsubscribe(event, callback) {
        if (this.subscribers[event]) {
            this.subscribers[event] = this.subscribers[event].filter(sub => sub !== callback);
        }
    }

    publish(event, data) {
        if (this.subscribers[event]) {
            this.subscribers[event].forEach(callback => callback(data));
        }
    }
}

// Usage
const publisher = new Publisher();
publisher.subscribe('greeting', data => console.log(`Hello, ${data.name}!`));
publisher.publish('greeting', { name: 'Alice' });
publisher.unsubscribe('greeting', data => console.log(`Hello, ${data.name}!`));

场景

发布订阅模式被广泛应用于各种编程环境。以下是一些常见的使用场景示例。

  1. 事件处理系统:发布-订阅模式在事件驱动的系统中被广泛使用,比如DOM事件监听。在网页开发中,你可以为特定的DOM元素添加事件监听器,比如点击、双击、鼠标悬停等。当这些事件被触发时,所有订阅了相应事件的函数都会被执行。
  2. 消息队列:消息队列是分布式系统中常用的一种数据交换形式。发布-订阅模式可以用于实现消息队列,发布者将消息发送到消息队列,然后消息队列将消息路由到一个或多个已经订阅了这些消息的订阅者。
  3. 网络请求:在AJAX请求中,可以使用发布-订阅模式处理异步操作。比如你可以为请求的开始、结束、成功和失败等事件注册回调函数。

事件总线

事件总线: 是实现发布-订阅模式的一种方式 , 提供了一个中心化的事件处理系统,可以处理大量的事件和消息传递,并且支持复杂的事件处理逻辑,如事件过滤、优先级等

观察者模式, 发布-订阅, 事件总线 傻傻分不清?先有问题再有答案 如何理解观察者模式? 有什么使用场景? 发布订阅模式

demo

class EventBus {
    constructor() {
        this.listeners = {};
    }

    on(event, callback) {
        if (!this.listeners[event]) {
            this.listeners[event] = [];
        }
        this.listeners[event].push(callback);
    }

    off(event, callback) {
        if (this.listeners[event]) {
            this.listeners[event] = this.listeners[event].filter(listener => listener !== callback);
        }
    }

    emit(event, data) {
        if (this.listeners[event]) {
            this.listeners[event].forEach(callback => callback(data));
        }
    }
}

// Usage
const eventBus = new EventBus();
eventBus.on('message', message => console.log(`Received message: ${message}`));
eventBus.emit('message', 'Hello, Event Bus!');
eventBus.off('message', message => console.log(`Received message: ${message}`));

区别总结

观察者模式:需要观察的对象维护一个观察者列表(直接耦合),当被观察对象的状态发生变化时,会逐一通知观察者列表中的观察者。因此,在观察者模式中,观察者和被观察者直接相互关联,他们之间的联系是直接的,构成一种 一对多的关系

发布-订阅模式:主题(发布者)和观察者(订阅者)之间的关系是通过调度中心(如事件队列、消息队列)进行联系的,他们之间 不直接关联。这样做可以减少订阅者和发布者之间的依赖性,降低他们之间的耦合度, 实现 多对多 的通信。

事件总线模式可以看作是发布-订阅模式的一种实现方式,它提供了一个事件总线,所有的发布者和订阅者都是通过这个事件总线进行通讯的。这样可以进一步减少组件之间的依赖性,所有的消息发送和接收都是通过事件总线进行的,使得组件之间不需要知道彼此的存在。

转载自:https://juejin.cn/post/7413284830946295817
评论
请登录