观察者模式和发布订阅模式
基本概念
发布订阅模式
在这个模式中,发布者(或者说是主题)并不直接发送消息给订阅者,而是通过调度中心(或者叫消息代理)来传递消息。 发布者(或者说是主题)并不知道订阅者的存在,而订阅者也不知道发布者的存在。他们彼此唯一的关系就是在调度中心注册成为订阅者或者发布者。
观察者模式
观察者(Observer)模式中包含两种对象,分别是目标对象和观察者对象。在目标对象和观察者对象间存在着一种一对多的对应关系,当这个目标对象的状态发生变化时,所有依赖于它的观察者对象都会得到通知并执行它们各自特有的行为。
区别
简单来讲,观察者模式是基于发布订阅模式的,发布订阅模式需要手动进行订阅和发布,是基于中间的调度栈进行的,可以不发布,也可以不订阅,发布订阅是解偶没有关联的,而观察者模式是有相互依赖关系的,并且观察者必须
实现一个update方法提供调用。
发布订阅者模式示例
- 如下,有两个异步获取文件的函数,在不使用promise的情况下,需要在读取到两个文件后执行finish。
const fs = require('fs'); // 引入node中的fs模块
fs.readFile('./1.text', 'utf8', function (error, data) {
console.log('读取到了文件1.text');
});
fs.readFile('./2.text', 'utf8', function (error, data) {
console.log('读取到了文件2.text');
});
function finish() {
console.log('文件读取完成');
}
- 利用发布订阅模式,实现一个中间调度栈
event
event
需要缓存发布的数据的数据和订阅回调函数event
需要实现on和emit方法提供发布和订阅功能on
接收一个函数作为参数,并且将该函数进行缓存,等待发布消息后按顺序执行。emit
接收key和value两个参数,作为发布的键值对数据进行缓存,订阅函数被执行是传入。
const event = {
_arr: [], // 用于缓存所有订阅的回调函数
data: {}, // 用于缓存发布的数据
on(fn) {
// 直接缓存订阅函数
this._arr.push(fn);
},
emit(key, value) {
// 发布数据,直接缓存数据并且执行所有的订阅函数。
this.data[key] = value;
this._arr.forEach(fn => fn(this.data));
}
}
- 最后使用该调度栈进行简单的发布订阅
const fs = require('fs'); // 引入node中的fs模块
const event = {
_arr: [], // 用于缓存所有订阅的回调函数
data: {}, // 用于缓存发布的数据
on(fn) {
// 直接缓存订阅函数
this._arr.push(fn);
},
emit(key, value) {
// 发布数据,直接缓存数据并且执行所有的订阅函数。
this.data[key] = value;
this._arr.forEach(fn => fn(this.data));
}
}
event.on((data) => { // 订阅
console.log('当前读取到的文件', data);
});
event.on((data) => { // 订阅
if (Reflect.ownKeys(data).length === 2) { // 等价于Object.Keys()
finish();
}
});
fs.readFile('./1.text', 'utf8', function (error, data) {
console.log('读取到了文件1.text');
event.emit('text1', '1111'); // 发布
});
fs.readFile('./2.text', 'utf8', function (error, data) {
console.log('读取到了文件2.text');
event.emit('text2', '2222'); // 发布
});
function finish() {
console.log('文件读取完成');
event.emit(); // 发布
}
观察者模式
- 观察者模式分为
观察者
和被观察者
两个类。 - 被观察者需要接收观察者。
- 观察者需要提供update方法。
// 被观察者
class Subject {
constructor() {
this.observers = []; // 用于接收观察者
this.state = false; // 当前的被观察者状态
}
attach(fn) {
this.observers.push(fn); // 订阅模式,被观察者接收观察者
}
setState(newValue) {
this.state = newValue; // 修改状态
// 当状态变化时,需要通知所有观察者,调用观察者的update方法.
this.observers.forEach(fn => fn.update(newValue));
}
}
// 观察者
class Observer {
update() {
console.log('收到了通知!');
}
}
const a = new Subject();
const b = new Observer();
const c = new Observer();
a.attach(o1); // 订阅
a.attach(o2); // 订阅
// 修改被观察者状态
a.setState(false);