观察者模式与发布-订阅模式

145 阅读2分钟

概念

image.png

观察者模式

在观察者模式中,只有两种主体:目标对象 (Subject) 和 观察者 (Observer)。

  • 目标对象 Subject:

    • 维护观察者列表 observers
    • 定义添加、移除观察者的方法
    • Subject调用 Subject.notify是会执行observers数组的回调
  • 观察者 Observer 需要实现 update 方法,供Subject调用。

发布-订阅模式

  • 发布 —— Publisher
  • 中介 —— EventChannel
    • 维护任务类型,以及每种任务下的订阅情况
    • 给订阅者提供订阅功能 —— subscribe 功能
    • 给发布者提供发布动能 —— publish 功能
  • 订阅 —— Subscriber

一、观察者模式

image.png

// 定义主题类
class Subject {
  constructor() {
    // 观察者list
    this.observers = [];
    console.log('Subject created');
  }
  // 增加观察者
  addObserver(observer) {
    console.log('Observer.add invoked');
    this.observers.push(observer);
  }
  // 移除观察者
  removeObserver(observer) {
    console.log('Observer.remove invoked');
    this.observers.forEach((item, i) => {
      if (item === observer) {
        this.observers.splice(i, 1);
      }
    });
  }
  // 通知所有观察者
  notify(...arg) {
    console.log('Subject.notify invoked');
    this.observers.forEach((observer) => {
      observer.update(...arg);
    });
  }
}

// 定义观察者类
class Observer {
  constructor() {
    console.log('Observer created');
  }

  update(...arg) {
    console.log('arg:', arg);
    console.log('Observer.update invoked');
  }
}
// 创建主题
const subject = new Subject();
// 创建观察者
const observer = new Observer();
// 主题添加观察者
subject.addObserver(observer);
// 主题通知观察者
subject.notify('notify...');

二、发布-订阅模式

image.png

事件调度中介

class EventChannel {
  constructor() {
    // 事件中心
    this.events = {};
  }

  // 订阅事件
  subscribe(eventName, callback) {
    if (!this.events[eventName]) {
      this.events[eventName] = [];
    }
    this.events[eventName].push(callback);
  }

  // 发布事件
  publish(eventName, ...args) {
    if (this.events[eventName]) {
      // 这里需要对 this.events[eventName] 做一次浅拷贝,主要目的是为了避免通过 once 安装的监听器在移除的过程中出现顺序问题
      const events = this.events[eventName].slice();
      events.forEach((callback) => {
        callback(...args);
      });
    }
  }

  // 取消订阅
  unSubscribe(eventName, callback) {
    const callbacks = this.events[eventName];
    const index = callbacks.indexOf(callback);
    if (index !== -1) {
      callbacks.splice(index, 1);
    }
  }

  // 当次订阅
  subscribeOnce(eventName, callback) {
    // 对回调函数进行包装,使其执行完毕自动被移除
    const wrapper = (...args) => {
      callback(...args);
      this.unSubscribe(eventName, wrapper);
    };
    this.subscribe(eventName, wrapper);
  }
}

1.生成事件频道

const eventChannel = new EventChannel();

2.创建订阅

// 创建订阅event-test
eventChannel.subscribe('event-test', () => {
  console.log('event-test1');
});
// 创建订阅event-prew
eventChannel.subscribe('event-prew', () => {
  console.log('event-prew1');
});

3.发布事件

// 发布事件
eventChannel.publish('event-prew');

总结

从表面上看:

  • 观察者模式里,只有两个角色 —— 观察者 + 被观察者
  • 而发布-订阅模式里,不仅仅只有发布者和订阅者两个角色,还有一个中介角色(EventChannel

往更深层次讲:

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

从使用层面上讲:

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

参考

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