前言
很多人都会认为观察者(Observer)模式等同于发布(Publish)/订阅(Subscribe)模式,发布订阅模式里的Publisher 就是观察者模式里的 Subject,而 Subscriber,就是 Observer。实际上,它们的实现思路是非常相似的,但是其流程结构是存在区别的。发布订阅模式可以说是派生自观察者模式的。
观察者模式
每一个观察者(Observer)实际上是一个实例对象,Subject 的通知实际上是调用观察者中的某个具体的方法,即其内部的更新接口。发布者和观察者实际上是存在依赖的,其必须知道观察者内部更新的接口方法是什么。观察者模式实现的是一种松散耦合。
因此观察者模式多用于单个应用内部。
// 发布者,发布者拥有观察者列表
class Subject{
constructor(){
this.observerList = [];// 观察者列表
}
addObserver(observer){
this.observerList.push(observer);
}
removeObserver(observer){
for (let i = 0; i < this.observerList.length; i++) {
// 判断是否是源函数,这里考虑了 once 的特殊情况
if (this.observerList[i] === observer || this.observerList[i].source === observer) {
this.observerList.splice(i, 1);
break;
}
}
}
notify(data){
this.observerList.forEach((observer) => {
observer.update(data);
})
}
}
// 观察者,每个观察者都需要一个更新接口的方法
class Observer{
constructor(){
}
update(){
// ...
};
}
发布订阅模式
发布订阅模式的订阅者实际上就是一个函数或者说方法,在发布者进行发布时,并不是直接通知订阅者,而是由调度中心(broker)来逐一调用订阅者,发布者不需要管订阅者是谁。也就是说发布订阅模式中,发布者和订阅者不是松耦合,而是完全解耦的。
发布订阅模式更像浏览器的事件机制,用户触发事件的发布,但是其不知道会发生什么事情,由浏览器进行对该事件的事件池进行逐一调用。
因此发布订阅模式多用于不同模块之间的解耦与通信。相比之下,发布订阅模式更为常用。
// 这是改进版的发布订阅模式
class Subscribe {
//=> []创建一个容器,管理需要执行的方法
//=> {} 实现多个不同类型容器
constructor() {
this.ponds = {};
}
//=> 订阅
on (type, listener) {
// listener 必须是函数
if (typeof listener !== "function")
throw new error("the second param of 'on' must be a function");
this.ponds[type] = this.ponds[type] || [];
// 判断事件池中是否已存在相同的 listener,存在则不添加
let n = this.ponds[type].indexOf(listener);
if (n === -1) {
this.ponds[type].push(listener);
}
return this;
}
//=> 订阅一次
once (type, listener) {
if (typeof listener !== "function")
throw new error("the second param of 'once' must be a function");
let _this = this;
let fn = () => {
// this 为 window
_this.off(type, listener);
listener.apply(_this, arguments);
}
fn.source = listener; // 将源函数挂载到 fn 上
return this.on(type, fn);
}
//=> 执行容器中所有的方法
// 参数为 type, ...args
emit (...args) {
let type = args.shift();
let listeners = this.ponds[type];
if (!listeners) return;
// 锁死队列,防止事件池中的函数不断向事件池添加订阅,出现死循环
listeners = listeners.slice();
// 进行逐个发布
listeners.forEach((item) => {
item(...args);
})
return this;
}
//=> 取消订阅
off (type, listener) {
let listeners = this.ponds[type];
if (!listeners) return this;
for (let i = 0; i < listeners.length; i++) {
// 判断是否是源函数,这里考虑了 once 的特殊情况
if (listeners[i] === listener || listeners[i].source === listener) {
listeners.splice(i, 1);
break;
}
}
if (listeners.length === 0) {
delete this.ponds[type]; // 防止空的时候还进行遍历判断
}
return this;
}
//=> 获取所有的订阅者
listeners (type) {
// 返回克隆数组
return (this.ponds[type] || []).slice();
}
}