一 观察者模式
1 含义
1 主体/被观察者- Subject
- addObs(ob): 添加ob
- removeObs(ob): 移除ob
- setData(): 更新数据,通常是触发notify的契机
- notifyObs(): 通知ob
2 观察者- Observer
- update()/ doSomething(): 回调函数
示意图见下:
2 实现流程/伪代码
1 流程
- S1 new Subject(), 记做s1 + new Observer(), 记做ob1 ==>
- S2 s1.addObs(ob1) ==>
- S3 s1.setData() ==> s1.notifyObs() ==>
- S4 ob1.doSomething()/ ob2.doSomething()……
2 伪代码- 观察者模式
// 被观察主体
class Subject {
constructor() {
this.obs = []
}
addObs(ob) {
this.obs.push(ob)
}
removeObs(ob) {
const index = this.obs.indexOf(ob)
index > -1 && this.obs.splice(index, 1)
}
notifyObs() {
this.obs.forEach(ob => ob.update(this))
}
}
// 观察者
class Observer {
constructor() {}
// 或者称为updata()
update(subject) {
console.log('去做点什么吧')
}
}
二 发布-订阅模式
1 含义
1 发布订阅中心
- typeTocbsMap: { type: [cb1, cb2 ….]}: 消息类型与回调队列 映射关系
- typeToMsgMap: { type: [content1, content2 ….]}: 消息类型与内容 映射关系
- subscribe(type, cb): 建立 1个消息类型与回调函数的 绑定关系
- publish(type, content): 建立 1个消息类型与消息内容的 绑定关系
- notify(type): 把 某个消息类型对应的 消息内容,下发给所有的 消息回调
2 发布者
- publish(type, content): 中转调用 sub.publish()
3 订阅者
- subscribe(type, cb): 中转调用 sub.subscribe(type, cb)
示意图见下:
2 实现流程/伪代码
1 流程
- S1 sub.subscribe(type, cb)==> PubSub.subscribe(type, cb)
- S2 pub.publish(type, content)==> PubSub.publish(type, cb)
- S3 更新数据后==> PubSub.notify(type)==> cbX(contents)
2 伪代码-发布订阅模式
// 发布订阅中心
class PubSub {
constructor() {
this.messages = {};
this.listeners = {};
}
publish(type, content) {
const existContent = this.messages[type];
if (!existContent) {
this.messages[type] = [];
}
this.messages[type].push(content);
}
subscribe(type, cb) {
const existListener = this.listeners[type];
if (!existListener) {
this.listeners[type] = [];
}
this.listeners[type].push(cb);
}
notify(type) {
const messages = this.messages[type];
const subscribers = this.listeners[type] || [];
subscribers.forEach((cb, index) => cb(messages));
}
}
// 发布者
class Publisher {
// ctx指向是其所属的 发布订阅中心实例对象
constructor(name, ctx) {
this.name = name;
this.ctx = ctx;
}
publish(type, content) {
this.ctx.publish(type, content);
}
}
// 订阅者
class Subscriber {
// ctx指向是其所属的 发布订阅中心实例对象
constructor(name, ctx) {
this.name = name;
this.ctx = ctx;
}
subscribe(type, cb) {
this.ctx.subscribe(type, cb);
}
}
三 观察者模式 和 发布-订阅模式的 区别
- 是否存在第三方角色/ 发布者是否可以直接与 订阅者通信:
- 观察者模式: 只有[观察者]和[被观察者] 2个角色;
- 发布-订阅模式: 有 [发布者]、[订阅者]、[调度中心/发布订阅中心] 3个角色
-
观察者模式是松耦合的,因为被观察者 内部包含一部分处理观察者关系的逻辑;
而 发布者和订阅者由于[发布订阅中心]的出现 而完全解耦了 -
观察者模式大多数是同步的,而发布-订阅模式大多数是异步的(使用消息队列)
四 其他相关内容
1 EventBus/ EventEmitter实现
1 伪代码- EventBus/EventEmitter
class EventEmiter {
constructor() {
// events是一个map,用于存储事件与回调之间的对应关系
this.events = {}
}
// 监听
on(eventName, cb) {
if (!this.events[eventName]) {
this.events[eventName] = []
}
this.events[eventName].push(cb)
}
// 移除某个事件回调队列里的指定回调函数
off(eventName, cb) {
const cbs = this.events[eventName]
const index = cbs.indexOf(cb)
if (index !== -1) {
cbs.splice(index, 1)
}
}
// 派发事件
emit(eventName, ...args) {
const cbs = this.events[eventName]
if (cbs) {
// 对cbs做一次浅拷贝
// 主要是为了 避免通过once安装的监听器在移除的过程中 出现顺序问题
const handlers = cbs.slice()
handlers.forEach(cb => cb(...args))
}
}
// 只调用一次
once(eventName, cb) {
const wrapper = (...args) => {
cb(...args)
this.off(eventName, wrapper)
}
this.on(eventName, cb)
}
}
五 参考文档
© 版权声明
文章版权归作者所有,未经允许请勿转载,侵权请联系 admin@trc20.tw 删除。
THE END