发布订阅模式、观察者模式和 EventBus、EventEmitter

观察者模式

通俗来讲,观察者模式指的是一个对象(Subject)维持一系列依赖于它的对象(Observer),当有关状态发生变更时 Subject 对象则通知一系列 Observer 对象进行更新。

在观察者模式中,Subject 对象拥有添加、删除和通知一系列 Observer 的方法等等,而 Observer 对象拥有更新方法等等。

Subject 对象添加了一系列 Observer 对象之后,Subject 对象则维持着这一系列 Observer 对象,当有关状态发生变更时 Subject 对象则会通知这一系列 Observer 对象进行更新。

js 复制代码
// Subject 对象
function Subject() {
  this.observers = [];
};

Subject.prototype = {
  add(observer) {  // 添加
    this.observers.push(observer);
  },
  notify() {  // 通知
    let observers = this.observers;
    for (let i = 0; i < observers.length; i++) {
      observers[i].update();
    }
  },
  remove(observer) {  // 删除
    let observers = this.observers;
    for (let i = 0; i < observers.length; i++) {
      if (observers[i] === observer) {
        observers.splice(i, 1);
      }
    }
  },
};

// Observer 对象
function Observer(name) {
  this.name = name;
};

Observer.prototype = {
  update() {  // 更新
    console.log('my name is ' + this.name);
  }
};

let sub = new Subject();
let obs1 = new Observer('obs1');
let obs2 = new Observer('obs2');
sub.add(obs1);
sub.add(obs2);
sub.notify();  //my name is obs1 name is obs2

发布订阅模式

发布订阅模式指的是希望接收通知的对象(Subscriber)基于一个主题通过自定义事件订阅主题,发布事件的对象(Publisher)通过发布主题事件的方式通知各个订阅该主题的 Subscriber 对象。

js 复制代码
const pubSub = {
  list: {},
  subscribe(key, fn) {  // 订阅
    if (!this.list[key]) {
      this.list[key] = [];
    }
    this.list[key].push(fn);
  },
  publish() {  // 发布
    const arg = arguments;
    const key = Array.prototype.shift.call(arg);
    const fns = this.list[key];

    if (!fns || fns.length <= 0) return false;

    for (let i = 0, len = fns.length; i < len; i++) {
      fns[i].apply(this, arg);
    }
  },
  unSubscribe(key) {  // 取消订阅
    delete this.list[key];
  }
};

// 进行订阅
pubSub.subscribe('name', (name) => {
  console.log('your name is ' + name);
});
pubSub.subscribe('sex', (sex) => {
  console.log('your sex is ' + sex);
});
// 进行发布
pubSub.publish('name', 'nameValue');  // your name is nameValue
pubSub.publish('sex', 'male');  // your sex is male

观察者模式 VS 发布订阅模式

个人理解,观察者模式维护的是一个事件和多个依赖这个事件的对象之间的关系:

csharp 复制代码
event->[obj1,obj2obj3,....]

发布订阅模式是多个事件(主题)和依赖于该事件(主题)的对象之间的关系:

css 复制代码
{  
    event1->[obj1,obj2....],  
    event2->[obj1,obj2.....],
    ....
}

从表面上看:

  • 观察者模式里,只有两个角色------观察者 + 被观察者
  • 发布订阅模式里面,却不仅仅只有发布者和订阅者两个角色,还有一个经常被我们忽略的------Broker

往更深层级讲:

  • 观察者和被观察者,是松耦合的关系
  • 发布者和订阅者,则完全不存在耦合

从使用层面上讲:

  • 观察者模式,多用于单个应用内部
  • 发布订阅模式,则更多的是一种跨应用的模式,比如我们常用的消息中间件

EventBus

EventBus 和发布订阅模式非常相似,比如 Vue 中的 EventBus ,可以通过 $on 来订阅不同类型的主题,通过 $emit 发布不同的主题,该主题的订阅者就会执行相应 callback 函数。

js 复制代码
class EventBus {
  constructor() {
    this.events = {}; // 存储事件及其对应的回调函数列表
  }

  // 订阅事件
  subscribe(eventName, callback) {
    this.events[eventName] = this.events[eventName] || []; // 如果事件不存在,创建一个空的回调函数列表
    this.events[eventName].push(callback); // 将回调函数添加到事件的回调函数列表中
  }

  // 发布事件
  publish(eventName, data) {
    if (this.events[eventName]) {
     this.events[eventName].forEach(callback => {
        callback(data); // 执行回调函数,并传递数据作为参数
      });
    }
  }

  // 取消订阅事件
  unsubscribe(eventName, callback) {
    if (this.events[eventName]) {
      this.events[eventName] = this.events[eventName].filter(cb => cb !== callback); // 过滤掉要取消的回调函数
    }
  }
}

// 使用

// 创建全局事件总线对象
const eventBus = new EventBus();

const callback1 = data => {
  console.log('Callback 1:', data);
};

const callback2 = data => {
  console.log('Callback 2:', data);
};

// 订阅事件
eventBus.subscribe('event1', callback1);
eventBus.subscribe('event1', callback2);

// 发布事件
eventBus.publish('event1', 'Hello, world!');

// 输出:
// Callback 1: Hello, world!
// Callback 2: Hello, world!

// 取消订阅事件
eventBus.unsubscribe('event1', callback1);

// 发布事件
eventBus.publish('event1', 'Goodbye!');

// 输出:
// Callback 2: Goodbye!

EventEmitter

js 复制代码
class EventEmitter {
  constructor() {
    this.events = {}; // 用于存储事件及其对应的回调函数列表
  }

  // 订阅事件
  on(eventName, callback) {
    this.events[eventName] = this.events[eventName] || []; // 如果事件不存在,创建一个空的回调函数列表
    this.events[eventName].push(callback); // 将回调函数添加到事件的回调函数列表中
  }

  // 发布事件
  emit(eventName, data) {
    if (this.events[eventName]) {
      this.events[eventName].forEach(callback => {
        callback(data); // 执行回调函数,并传递数据作为参数
      });
    }
  }

  // 取消订阅事件
  off(eventName, callback) {
    if (this.events[eventName]) {
      this.events[eventName] = this.events[eventName].filter(cb => cb !== callback); // 过滤掉要取消的回调函数
    }
  }
  
  // 添加一次性的事件监听器 
  once(eventName, callback) { 
      const onceCallback = data => { 
          callback(data); // 执行回调函数 
          this.off(eventName, onceCallback); // 在执行后取消订阅该事件 
      }; 
      this.on(eventName, onceCallback); 
  }
}

//  使用
    const emitter = new EventEmitter();

    const callback1 = data => {
      console.log('Callback 1:', data);
    };
   
    const callback2 = data => {
      console.log('Callback 2:', data);
    };
    
    // 添加一次性事件监听器 
    const onceCallback = data => { 
        console.log('Once Callback:', data); 
    };
    
    // 订阅事件
    emitter.on('event1', callback1);
    emitter.on('event1', callback2);
    emitter.once('event1', onceCallback);
    
    // 发布事件
    emitter.emit('event1', 'Hello, world!');

    // 输出:
    // Callback 1: Hello, world!
    // Callback 2: Hello, world!
    // Once Callback: Hello, world!
    
    // 取消订阅事件
    emitter.off('event1', callback1);

    // 发布事件
    emitter.emit('event1', 'Goodbye!');

    // 输出:
    // Callback 2: Goodbye!

EventBusEventEmitter 有什么区别

至于EventBusEventEmitter 有什么区别,目前除了EventEmitter可以添加一次性监听事件,其余的我还没体会到,欢迎各位大佬指点~~

相关推荐
dlnu201525062226 分钟前
ssr实现方案
前端·javascript·ssr
古木201930 分钟前
前端面试宝典
前端·面试·职场和发展
Damon_X2 小时前
桥接模式(Bridge Pattern)
设计模式·桥接模式
轻口味2 小时前
命名空间与模块化概述
开发语言·前端·javascript
前端小小王3 小时前
React Hooks
前端·javascript·react.js
迷途小码农零零发3 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
真滴book理喻6 小时前
Vue(四)
前端·javascript·vue.js
越甲八千6 小时前
重温设计模式--享元模式
设计模式·享元模式
程序员_三木6 小时前
Three.js入门-Raycaster鼠标拾取详解与应用
开发语言·javascript·计算机外设·webgl·three.js
码农爱java8 小时前
设计模式--抽象工厂模式【创建型模式】
java·设计模式·面试·抽象工厂模式·原理·23种设计模式·java 设计模式