观察者模式和发布订阅模式(上)
2019-12-02

观察者模式

定义:观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。其中有两个定义需要明确,被观察者和观察者。通常来说,这两者是一对多的,也有多对多的情景。在网页开发中,被观察者通常是数据源,不论是内存数据,还是持久化数据,又或者是接口返回的数据,都可以作为被观察者。它一旦改变,就去改变依赖于它的节点。观察者有很多可能,针对于网页开发,我们常常认为dom节点是观察者,一旦节点的监视的数据源发生变化,节点也执行更新方法。当然不限于此,也有可能是一个事件,一次计数等等。接下来用js写一个简单的观察者模式的例子:

// 发布类class Subject { constructor (data) { this.obList = []; this.data = data; } add (ob) { if (arguments.length >= 1) { Array.from(arguments).forEach(item => this.obList.push(item)); } } remove (ob) { let i = this.obList.findIndex(ele => ele === ob); if (i >= 0) { this.obList.splice(i, 1); } } notify () { this.obList.forEach((item) => { item.update(this.data); }) }}// 观察者类class Observer { constructor (id) { this.id = id; } update (data) { console.log("observer " + this.id + ": " + data + ";"); }}function test() { let sub = new Subject("test"); let ob1 = new Observer(1); let ob2 = new Observer(2); let ob3 = new Observer(3); sub.add(ob1, ob2, ob3); sub.notify(); sub.remove(ob2); sub.notify();}test();

结果为:

observer 1: test;observer 2: test;observer 3: test;observer 1: test;observer 3: test;

这里简单定义了一个发布类和一个观察类,发布者维护一个观察者列表,每次数据变化后,依次通知所有在观察者列表里的观察者。代码很简单,可以执行在控制台或者node里跑一下。但是这样的耦合很深,观察者和发布者不能有其他的表现,很死板,我们可以继续抽象一下。先画个类图:借助于TypeScript,我们可以有如下的发布者和观察者定义。

abstract class Observer { abstract update();}abstract class Subject { protected obList: ObserverList; abstract notify();}

ObserverList则可以实现如下:

class ObserverList { private list: Array<Observer>; constructor () { this.list = []; } add (ob: Observer) { this.list.push(ob); } remove (ob: Observer) { if (this.list.indexOf(ob) > -1) { this.list.splice(this.list.indexOf(ob), 1); } } empty () { this.list = []; } public each () { this.list.forEach(item => { item.update(); }) }}

接下来实现两个实体类:

// 实体发布类class ConcreteSubject extends Subject { protected obList = new ObserverList(); private _data: string; constructor (defaultData: string) { super(); this._data = defaultData; } set data (newVaule) { this._data = newVaule; } get data () { return this._data; } add (ob: Observer) { this.obList.add(ob); } remove (ob: Observer) { this.obList.remove(ob); } notify () { this.obList.each() }}// 可以指定发布者的观察者类class ConcreteObserver extends Observer { readonly _id; private sub; constructor (id, sub) { super(); this._id = id; this.sub = sub; } get id () { return this._id; } update () { console.log("concrete observer " + this.id + ": " + this.sub.data); }}

跑一下测试代码:

let sub = new ConcreteSubject("test");let ob1 = new ConcreteObserversub);let ob2 = new ConcreteObserversub);let ob3 = new ConcreteObserversub);sub.add(ob1)sub.add(ob2)sub.add(ob3)sub.notify();

上面的发布类,使用add、remove等方法来处理观察者列表,通过notify方法,则去通知观察者们,可以去执行update方法了。观察者和被观察者,仍然耦合比较深,所以又有人提出来发布订阅模式,维护一个事件中心,来处理多个观察者和被观察者的关系,不让他们直接耦合在一起。下一篇对发布订阅做解析。