设计模式-订阅发布和观察者区别

69 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动

开发者常常将订阅发布模式和观察者模式混淆,其实这两者存在一些实现上的差异,常常混淆的原因是因为其概念的通用

订阅发布是⼀种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。⽽是将发布的消息分为不同的类别,然后分别发送给不同的订阅者。在该模式中会存在三个角色:发布者、订阅者和通道(用于控制消息的订阅和发布)

观察者模式是⼀种⼀对多的关系,让多个观察者对象同时监听某⼀个主题对象,这个主题对象的状态发⽣变化时就会通知所有的观察者对象,使得它们能够⾃动更新⾃⼰。在该模式中会存在两个角色:观察者和被观察者

订阅发布

首先看一下常规的订阅发布实现


function EventEmitter() {
  this._events = Object.create(null)
}

// 订阅
EventEmitter.prototype.on = function (eventName, fn) {
  // 若不是直接 new EventEmitter,而是通过继承,可能导致没有 _events,需要处理一下
  if(!this._events) {
    this._events = Object.create(null)
  }
  
  // 处理 newListener
  if(eventName !== 'newListener'){
    this.emit('newListener', eventName)
  }

  if(this._events[eventName]){
    this._events[eventName].push(fn)
  }else {
    this._events[eventName] = [fn]
  }
}

// 发布
EventEmitter.prototype.emit = function (eventName, ...args) {
  if(this._events[eventName]){
    this._events[eventName].forEach(fn => fn(...args))
  }
}

// 订阅 - 仅触发一次
EventEmitter.prototype.once = function (eventName, fn) {
  const one = (...args) => {
    fn(...args)
    this.remove(eventName, one)
  }

  one.fn = fn

  this.on(eventName, one)
}

// 移除订阅
EventEmitter.prototype.remove = function (eventName, fn) {
  if(this._events[eventName]){
    this._events[eventName] = this._events[eventName].filter(cb => cb !== fn && cb.fn !== fn) 
  }
}

验证一下效果

const em = new EventEmitter();

em.on("one", () => {
	console.log("触发one事件");
});

em.on("one", () => {
	console.log("触发one事件");
});

em.emit("one");

控制台输出两次触发one事件

观察者

接下来实现观察者模式

/** 
 * 被观察者
*/
class Subject {
  constructor(name) {
    this.name = name
    this.arr = []
    this.state = '正常'
  }

  attach(o) {
    this.arr.push(o)
  }

  setState (newState){
    this.state = newState;
    
    this.arr.forEach(o => {
      o.update(newState);
    });
  }
}

/** 
 * 观察者
*/
class Observer {
  constructor(name) {
    this.name = name;
  }

  update(newState) {
    console.log(`观察者${this.name}接收到了被观察者的信息${newState}`);
  }
}

验证一下效果

const s = new Subject()
const o1 = new Observer('nordon')
const o2 = new Observer('wy')

s.attach(o1)
s.attach(o2)

s.setState('新的状态')

控制台会输出:

观察者nordon接收到了被观察者的信息新的状态

观察者wy接收到了被观察者的信息新的状态

通过attach添加的被观察者自动触发

综上可以看出两者存在的区别还是较为显著的,借用一句话概括两者的区别:有没有中间商赚差价