觀察者模式 (Observer pattern) 2

一個主題可能有多個觀察者,例如技術分析 ( 觀察者1 ),當日走勢 (觀察者 2) 等多個觀察者關注相同的主題。

Observer pattern

因為上一篇的設計沒有 Observer 抽象基礎類別,為了滿足多個觀察者的需求,或許會想到可以新增成員變數 ViewType 來區分不同的觀察者,擁有 $View_TA 的是技術分析,而擁有 $View_Tick 的是當日走勢。

enum class ViewType
{
	$View_TA, // 技術分析
	$View_Tick // 當日走勢
};

class CObserver final // 台積電資料的觀察者
{
public:
	CObserver(ViewType eType) : m_eType(eType)
	{}

public:
	void Update(CTSMCStockSubject* pSubject)
	{}

private:
	ViewType m_eType;
};
int main()
{
	auto ob1 = std::make_shared<CObserver>(ViewType::$View_TA); // 技術分析
	auto ob2 = std::make_shared<CObserver>(ViewType::$View_Tick); // 當日走勢

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

但這樣勢必 CObserver 內會有許多 if … else … 的判斷敘述去處理 $View_TA 及 $View_Tick 的分支 case,或許還有人進一步想到用狀態模式 (State pattern) 來重構 CObserver。

其實這些都是本末倒置,正確做法就是需要有 Observer 抽象基礎類別的存在,不同的觀察者繼承此抽象基礎類別並實作之,對應的處理邏輯寫在各自的衍生類別內,消除了 CObserver 內可能的 if (ViewType == $View_TA) else … 的判斷處理。

class IObserver // Observer 抽象基礎類別
{
public:
	virtual ~IObserver() = 0
	{}

public:
	virtual void Update(CTSMCStockSubject* pSubject) = 0;
};

// 技術分析
class CTAViewObserver : public IObserver
{
public:
	virtual void Update(CTSMCStockSubject* pSubject) override
	{}
};

// 商品盤勢
class CTickViewObserver : public IObserver
{
public:
	virtual void Update(CTSMCStockSubject* pSubject) override
	{}
};

CTSMCStockSubject 類別內所有的 CObserver 也要修改成 IObserver。

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

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

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

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

使用方式改為直接 new 出對應的 class,不必再使用 ViewType 區分,程式更為清楚明確。

int main()
{
	auto ob1 = std::make_shared<CTAViewObserver>(); // 技術分析
	auto ob2 = std::make_shared<CTickViewObserver>(); // 當日走勢

	CTSMCStockSubject subject;
	subject.Subscribe(ob1);
	subject.Subscribe(ob2);
	subject.Notify();
	subject.Unsubscribe(ob2);
	subject.Unsubscribe(ob1);
}

發佈留言