观察者模式
观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新。
观察者模式属于行为型模式,行为型模式关注的是对象之间的通讯,观察者模式就是观察者和被观察者之间的通讯。
被观察者:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Subject { constructor() { this.observerList = []; }
addObserver(observer) { this.observerList.push(observer); }
removeObserver(observer) { const index = this.observerList.findIndex(o => o.name === observer.name); this.observerList.splice(index, 1); }
notifyObservers(message) { const observers = this.observeList; observers.forEach(observer => observer.notified(message)); } }
|
观察者:
1 2 3 4 5 6 7 8 9 10 11 12
| class Observer { constructor(name, subject) { this.name = name; if (subject) { subject.addObserver(this); } }
notified(message) { console.log(this.name, 'got message', message); } }
|
使用代码如下:
1 2 3 4 5 6 7
| const subject = new Subject(); const observerA = new Observer('observerA', subject); const observerB = new Observer('observerB'); subject.addObserver(observerB); subject.notifyObservers('Hello from subject'); subject.removeObserver(observerA); subject.notifyObservers('Hello again');
|
上述代码中,观察者主动申请加入被观察者的列表,被观察者主动将观察者加入列表
发布订阅模式
发布-订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在。
发布者和订阅者需要通过发布订阅中心进行关联,发布者的发布动作和订阅者的订阅动作相互独立,无需关注对方,消息派发由发布订阅中心负责。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 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[index])); } }
|
发布者代码如下:
1 2 3 4 5 6 7 8 9
| class Publisher { constructor(name, context) { this.name = name; this.context = context; } publish(type, content) { this.context.publish(type, content); } }
|
订阅者代码如下:
1 2 3 4 5 6 7 8 9
| class Subscriber { constructor(name, context) { this.name = name; this.context = context; } subscribe(type, cb) { this.context.subscribe(type, cb); } }
|
使用代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| const TYPE_A = 'music'; const TYPE_B = 'movie'; const TYPE_C = 'novel';
const pubsub = new PubSub();
const publisherA = new Publisher('publisherA', pubsub); publisherA.publish(TYPE_A, 'we are young'); publisherA.publish(TYPE_B, 'the silicon valley'); const publisherB = new Publisher('publisherB', pubsub); publisherB.publish(TYPE_A, 'stronger'); const publisherC = new Publisher('publisherC', pubsub); publisherC.publish(TYPE_C, 'a brief history of time');
const subscriberA = new Subscriber('subscriberA', pubsub); subscriberA.subscribe(TYPE_A, res => { console.log('subscriberA received', res) }); const subscriberB = new Subscriber('subscriberB', pubsub); subscriberB.subscribe(TYPE_C, res => { console.log('subscriberB received', res) }); const subscriberC = new Subscriber('subscriberC', pubsub); subscriberC.subscribe(TYPE_B, res => { console.log('subscriberC received', res) });
pubsub.notify(TYPE_A); pubsub.notify(TYPE_B); pubsub.notify(TYPE_C);
|
区别
- 在观察者模式中,观察者是知道被观察者的,被观察者一直保持对观察者发送记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在,它们只有通过消息代理进行通信。
- 在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。
- 观察者模式大多数时候是同步的,比如当事件触发,被观察者就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)
vue中的应用
vue中数据响应式是观察者模式。
Vue2 的响应式是通过 Object.defineProperty 对数据进行劫持,并结合观察者模式实现。 Vue 利用 Object.defineProperty 创建一个 observe 来劫持监听所有的属性,把这些属性全部转为 getter 和 setter。Vue 中每个组件实例都会对应一个 watcher 实例,它会在组件渲染的过程中把使用过的数据属性通过 getter 收集为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。
vue中的事件总线就是使用的发布订阅模式。