发布订阅模式和观察者模式
发布订阅和观察者模式
曾经我一直以为发布订阅和观察者是一回事,而且有些书和博客里面也是这样说的,直到我看到了这篇剖析 Vue 原理&实现双向绑定 MVVM的文章,作者很细致的解读了 vue 的双向绑定实现原理,并用简约的代码实现了一个简化版的 vue,包括了解析器 Compile ,监听器 Observer,订阅者 Watcher 和 一个 vue 构造函数. 虽然很简介,但是我一直搞的不是很明白,对于发布订阅加数据劫持的原理是懂非懂,最近又偶然会看到这篇文章Observer vs Pub-Sub pattern,再结合上面的文章才算真的理解了发布订阅加数据劫持实现双向绑定的原理。
发布订阅模式 (wiki )
发布订阅最大的优点就是,发布者(Publisher)和订阅者(Subscriber)解耦,发布者发布消息到一个中间的消息代理,然后订阅者向该消息代理注册订阅,由消息代理来进行过滤。消息代理通常执行存储转发的功能将消息从发布者发送到订阅者。
class Publisher {
// 消息发布
constructor() {
this.MiddleProxy = new MiddleProxy();
}
register(action) {
// 在一定条件下去注册一个
if (action === 'needer') {
const subscriber = new Subscriber(action, () => {
console.log(action);
});
this.MiddleProxy.addSub(subscriber);
}
}
publish(action) {
this.MiddleProxy.notify(action);
}
}
class MiddleProxy {
// 消息代理中间件
constructor() {
this.subs = []; // 存放订阅者的实例
}
addSub(sub) {
this.subs.push(sub);
}
notify(action) {
this.subs.filter(sub => (sub.action === action)).forEach(sub => sub.run());
}
}
class Subscriber {
// 订阅者
constructor(action, callback) {
this.action = action;
this.callback = callback;
}
run() {
this.callback();
}
}
观察者模式(wiki)
观察者模式中消息的发布者(Subject)和观察者(Observer)是紧耦合的关系,在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实时事件处理系统。
class Subject {
constructor() {
this._observers = [];
}
subscribe(observer) {
this._observers.push(observer);
}
unsubscribe(observer) {
this._observers = this._observers.filter(obs => observer !== obs);
}
fire(change) {
this._observers.forEach(observer => {
observer.update(change);
});
}
}
class Observer {
constructor(state) {
this.state = state;
this.initialState = state;
}
update(change) {
let state = this.state;
switch (change) {
case 'INC':
this.state = ++state;
break;
case 'DEC':
this.state = --state;
break;
default:
this.state = this.initialState;
}
}
}
比较
发布订阅模式和观察者最大的区别就是,发布订阅模式的消息的发布者和消息的订阅者之间的关系,发布订阅是提供一个中间消息代理,通过消息代理实现订阅者的注册,并通过中间件来广播订阅者,而观察者模式是消息的发布者直接存储订阅者实例,直接触发订阅者的更新
发布订阅模式的优缺点都在于发布者和订阅者的解耦上
优点
- 实现时间上的解耦(组件,模块之间的异步通讯)
- 对象之间的解耦,交由发布订阅的对象管理对象之间的耦合关系
缺点
- 创建订阅者本身会消耗内存,订阅消息后,也许,永远也不会有发布,而订阅者始终存在内存中
- 对象之间解耦的同时,他们的关系也会被深埋在代码背后,这会造成一定的维护成本