JS設計模式——觀察者模式

什麼是觀察者模式?

一個或多個觀察者對目標的狀態感興趣,它們通過將自己依附在目標對象上以便註冊所感興趣的內容。
目標狀態發生改變並且觀察者可能對這些改變感興趣,就會發送一個通知消息,調用每個觀察這的更新方法。
當觀察者不再對目標狀態感興趣時,他們可以簡單地將自己從中分離。

看完後你會覺得這是什麼破玩意?不要急,我們舉個現實中的例子,來按段落順序逐步分解下上面這段話的意思。

去飯館吃飯,點完餐會給你一個號碼,然後就和其餘點完餐的人一樣,坐着等叫號。

每當做好一份餐,服務員就會喊多少號的餐好了。然後每個人都會收到這個消息,並開始檢查自己手裏的號,看是不是自己的餐好了,如果是自己的餐,就不要坐着了,趕緊去吃飯了。

有些人等着等着,覺得不想吃了,可能是嫌做飯太慢氣飽了,就離開不吃了。

抽象組件

我們把上面這個場景再簡化抽象成 JS 描述。

比如,一個 JS 對象 O 被修改了,那麼它就需要自動通知那些依賴它的對象。這裏所有依賴對象 O 的對象都是觀察者,而對象 O 就是一個目標。
在這裏插入圖片描述
根據這個圖,我們就可以假設對象 O 都是通過目標類(Subject Class)實例化出來的,而依賴對象 O 的那些對象都是通過觀察者類(Observer Class)實例化出來的。

那麼我們就可以抽象出來 4 個組建:

  • 目標(Subject)
    維護一系列的觀察者,方便添加或刪除觀察者。對應一個目標類,提供一些註冊和通知觀察者的接口。

  • 觀察者(Observer)
    在目標狀態發生改變時,爲需要得到通知的對象提供一個更新接口。對應一個觀察者類,提供一個更新觀察者狀態的接口。

  • 具體目標(ConcreteSubject)
    狀態發生改變時,向 Observer 發出通知,存儲 ConcreteObserver 的狀態。對應一個目標實例。

  • 具體觀察者(ConcreteObserver)
    存儲一個指向 ConcreteSubject 的引用,實現 Observer 類的更新接口,以是自身狀態與目標狀態保持一致。對應一個觀察者實例。

實現

1、爲了管理觀察者,我們先實現一個 ObserverList 類。也就是一個數組結構,並提供一些常用的數組操作接口。

class ObserverList {
	constructor() {
		this.observerList = []
	}
	add(obj) {
		return this.observerList.push(obj)
	}
	get(index) {
		if (index > -1 && index < this.observerList.length) {
			return this.observerList[index]
		}
	}
	count() {
		return this.observerList.length
	}
	remove(index) {
		this.observerList.splice(index, 1)
	}
	indexOf(obj) {
		return this.observerList.indexOf(obj)
	}
}

2、目標類。提供註冊和通知觀察者等接口。

class Subject {
	constructor() {
		this.observers = new ObserverList()
	}
	addObserver(observer) {
		this.observers.add(observer)
	}
	removeObserver(observer) {
		let index = this.observers.indexOf(observer)
		if (index >= 0) {
			this.observers.remove(index)
		}
	}
	notify() {
		const observerCount = this.observers.count()
		for (let i = 0; i < observerCount; i++) {
			this.observers.get(i).update(context)
		}
	}
}

3、Observer 類。提供一個觀察者更新接口。

class Observer {
	update() {
		// ...
	}
}

以上便是觀察者模式的實現方式。

使用

下面我們來看下觀察模式的使用,我們繼續前面飯館吃飯的場景。

<button id="callClient">Call</button>
// 先實例化一個飯館
const restaurant = new Subject()

// 實例化兩個顧客 a 和 b
const a = new Observer()
const b = new Observer()

// 定義當前被叫號的顧客
let currentObserver

/**
* @desc a 顧客實現自己的更新方法
* @params restaurant 飯堂實例
*/ 
a.update = function(restaurant) {
	// 判斷當前的叫號是不是自己
    if (restaurant.observers.get(0) == a) {
        console.log('我是a,我的飯好了')
        // 如果是自己,把當前叫號的顧客賦值成自己
        currentObserver = a
    }
}

// a 顧客實現自己的更新方法
b.update = function(restaurant) {
	// 判斷當前的叫號是不是自己
    if (restaurant.observers.get(0) == b) {
        console.log('我是b,我的飯好了')
        // 如果是自己,把當前叫號的顧客賦值成自己
        currentObserver = b
    }
}

// 兩位顧客先後在飯堂點餐,把自己註冊爲觀察者
restaurant.addObserver(a)
restaurant.addObserver(b)

// 綁定通知,每次點擊通知所有顧客,餐好了
callClient.onclick = function() {
	// 飯好了,通知所有顧客
    restaurant.notify(restaurant)
    // 把拿走飯的顧客,移除,不需要關心了
    restaurant.removeObserver(currentObserver)
}

以上就是一個模擬場景,應用了觀察者模式。如果有不正確的地方,望指出。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章