觀察者模式 (Observer pattern) 1

開發看盤軟體一定會用到的設計模式 (Design Pattern)。

Observer pattern

Wiki 的介紹,本文章從省略 Observer 及 Subject 抽象基礎類別 (Abstract Class) 開始講起,一步步推導出觀察者模式,此模式是一種一對多的依賴關係。

開發看盤軟體時,會需要觀看股票,例如想看台積電 (2330.TW) 的資料,因此有必要使用訂閱 – 等待資料更新通知的設計方式。其中主題類別是被觀察者訂閱的,也就是範例中台積電這個主題 (CTSMCStockSubject ),假設現在陽春版的看盤軟體只有一種頁面,也就是只有一個觀察者,那麼確實可以省略 Observer 及 Subject 抽象基礎類別 (Abstract Base Class) 。

P.S. 這裡需要注意,股價資料在盤中是會不斷的更新並通知,如果只需要一次性通訊,例如可能是等待歷史資料完成,那麼使用 std::future 更為適合。

觀察者類別 (CObserver),有一個等待資料更新的函式 (Update),這是會被主題類別呼叫的函式,參數可能會因為需求傳入某種資料格式或是主題類別的指標,稍後再用此指標去取得資料。

class CObserver final // 台積電資料的觀察者
{
public:
	void Update(CTSMCStockSubject* pSubject)
	{}
};

主題類別 (CTSMCStockSubject) 有三個函式,訂閱 (Subscribe)、取消訂閱 (Unsubscribe) 及通知觀察者們 (Notify)。

觀察者訂閱主題時,將自己傳進主題類別內 void Subscribe(const std::shared_ptr<CObserver>& ob),取消訂閱亦然。

class CTSMCStockSubject final // 台積電資料的主題
{
public:
	void Subscribe(const std::shared_ptr<CObserver>& ob)
	{
		m_observers.emplace(ob);
	}

	void Unsubscribe(const std::shared_ptr<CObserver>& ob)
	{
		m_observers.erase(ob);
	}

	void Notify()
	{
		for (auto& ob : m_observers)
		{
			ob->Update(this);
		}
	}

private:
	std::set<std::shared_ptr<CObserver>> m_observers;
};

我們發現紀錄主題類別的觀察者成員變數是使用 std::set,而且元素是 std::shared_ptr<…>,這是為了避免重複訂閱及提供取消訂閱的簡單實作。

std::set 因為元素即 key,因此可以避免重複訂閱。

使用 std::set (O(log N)) 比起 std::vector (O(N)) 可以更快速的遍歷觀察者們,而使用 std::shared_ptr 是為了讓觀察者可以被比較,找到要取消訂閱的元素。

int main()
{
	auto ob = std::make_shared<CObserver>();

	CTSMCStockSubject subject;
	subject.Subscribe(ob);
	subject.Notify();
	subject.Unsubscribe(ob);
	return 0;
}

發佈留言