观察者模式
定义:观察者模式(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 ConcreteObserver(1, sub);
let ob2 = new ConcreteObserver(2, sub);
let ob3 = new ConcreteObserver(3, sub);
sub.add(ob1)
sub.add(ob2)
sub.add(ob3)
sub.notify();
上面的发布类,使用add、remove等方法来处理观察者列表,通过notify方法,则去通知观察者们,可以去执行update方法了。
观察者和被观察者,仍然耦合比较深,所以又有人提出来发布订阅模式,维护一个事件中心,来处理多个观察者和被观察者的关系,不让他们直接耦合在一起。下一篇对发布订阅做解析。