觀察者模式 定義:觀察者模式(Observer Pattern):定義對象間的一種一對多依賴關係,使得每當一個對象狀態發生改變時,其相關依賴對象皆得到通知並被自動更新。 其中有兩個定義需要明確,被觀察者和觀察者。通常來說,這兩者是一對多的,也有多對多的情景。 在網頁開發中,被觀察者通常是數據源,不論 ...
觀察者模式
定義:觀察者模式(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方法了。
觀察者和被觀察者,仍然耦合比較深,所以又有人提出來發佈訂閱模式,維護一個事件中心,來處理多個觀察者和被觀察者的關係,不讓他們直接耦合在一起。下一篇對發佈訂閱做解析。