一、定义
观察者模式(Observer Pattern)
观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新。 观察者模式属于行为型模式,行为型模式关注的是对象之间的通讯,观察者模式就是观察者和被观察者之间的通讯。
发布订阅模式(Pub-Sub Pattern)
说明:发布订阅模式只是观察者模式的一个别称。 但是经过时间的沉淀,似乎他已经强大了起来,已经独立于观察者模式,成为另外一种不同的设计模式。
在现在的发布订阅模式中,称为发布者的消息发送者不会将消息直接发送给订阅者,这意味着发布者和订阅者不知道彼此的存在。在发布者和订阅者之间存在第三个组件,称为消息代理或调度中心或中间件,它维持着发布者和订阅者之间的联系,过滤所有发布者传入的消息并相应地分发它们给订阅者。
注:现实中的发布订阅模式的应用:
张三女朋友要张三买一辆保时捷,张三带着女朋友来到4S店才被告知,那款车现在没货,要过一段时间才有新车到。于是张三要了销售员联系方式,每天都问一遍销售新车到了没。但是李四、王五也每天问4S店销售。这样4S店的工作量很大并且重复。于是4S店销售就记下了所有要买车的意向客户的联系方式,车子以到就挨个给他们发消息通知他们。 这个小故事里就运用了发布订阅的模式
二、优缺点及使用场景
优点:
- 观察者和被观察者是抽象耦合的;
- 建立一套触发机制。
缺点:
- 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间;
- 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃;
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景(案例): 比如Node.js的事件驱动程序。
三、使用发布订阅模式实现on,emit,once,off函数
/*
订阅:
on(type,handler):监听event事件,事件触发时调用fn函数;
发布:
emit(type,arg1,arg2,arg3...):触发event事件,并把参数arg1,arg2,arg3....传给事件处理函数;
once(type,handler):为指定事件注册一个单次监听器,单次监听器最多只触发一次,触发后立即解除监听器;
off(type,handler):停止监听某个事件。
*/
class EventEmitter {
constructor() {
// 用来存放 订阅 和 发布的关系的
this.eventMap = {};
}
// type 这里就代表事件的名称
on(type, handler) {
// 判断 handler 的实例 是不是一个函数
if (!(handler instanceof Function)) {
throw new Error("请传入一个函数");
}
// 判断 eventMap 中是否有这个类型
if (!this.eventMap[type]) {
// 若不存在,新建该队列
this.eventMap[type] = [];
}
// 若存在,直接往队列里推入 handler
this.eventMap[type].push(handler);
}
//向上派发 消息出去
emit(type, ...params) {
// 假设该事件是有订阅的(对应的事件队列存在)
if (this.eventMap[type]) {
// 将事件队列里的 handler 依次执行出队
this.eventMap[type].forEach((handler, index) => {
// 这里携带参数, 触发on方法的handler 回调
handler(...params);
});
}
}
// 用来 卸载掉 不用监听的方法
off(type, handler) {
if (this.eventMap[type]) {
this.eventMap[type].splice(this.eventMap[type].findIndex(handler), 1);
}
}
once(type, handler) {
let wrapFunc = (...args) => {
handler.apply(this, args);
this.off(type, wrapFunc);
}
this.on(type, wrapFunc);
// return this;
}
}
const event1 = new EventEmitter();
// 编写一个简单的 handler
const testHandler = function (params1, params2) {
console.log(`test被触发了,参数值1是${params1},参数2是${params2}`);
};
const onceHandler = function(params) {
console.log(`Once的参数是:${params}`);
}
// 监听事件
event1.on("test", testHandler);
event1.once("once1", onceHandler);
// 在触发事件的同时,传入希望 testHandler 感知的参数
event1.emit("test", "小红", "小黑");
event1.emit("once1","xiaoming");
四、定义观察者模式和发布订阅模式有什么区别
观察者模式: 观察者(Observer)直接订阅(Subscribe)主题(Subject),而当主题被激活的时候,会触发(Fire Event)观察者里的事件。
发布订阅模式: 订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Topic),当发布者(Publisher)发布该事件(Publish topic)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。