在上一篇我們推導出需要 Observer 抽象基礎類別的原因,本篇我們來探討需要 Subject 抽象基礎類別的原因。
主題通知觀察者資料更新有兩種方式,一種是直接將資料推 (push) 送給觀察者;另一種是先通知觀察者,等觀察者有時間再回主題拉 (pull) 資料。
如果是將資料推 (push) 送給觀察者,Update 需要修改為傳遞資料參數,如下:
class IObserver{public: virtual ~IObserver() = 0 {}public: virtual void Update(double dPrice) = 0;};
// 技術分析class CTAViewObserver : public IObserver{public: virtual void Update(double dPrice) override {}};
開發看盤軟體時,單一頁面觀察者不會只顯示一檔股票, 如果我們的觀察者對多個股票主題有興趣會怎麼樣呢?假設現在關注的主題有兩個,台積電與鴻海。
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) { double dPrice = 300; ob->Update(dPrice); } }private: std::set<std::shared_ptr<IObserver>> m_observers;};
class CFoxconnStockSubject final // 鴻海{public: void Notify() { for (auto& ob : m_observers) { double dPrice = 70; ob->Update(dPrice); } } // 其他程式碼省略...};
注意 Notify 內呼叫的 Update 並沒有傳識別參數,那麼觀察者又怎麼知道是那個主題通知它呢?因此我們可能會修改為多傳一個股票代碼 (sStockID) 給 Update。
class IObserver{public: virtual ~IObserver() = 0 {}public: virtual void Update(const std::string& stockID, double dPrice) = 0;};
// 技術分析class CTAViewObserver : public IObserver{public: virtual void Update(const std::string& stockID, double dPrice) override {}};
那如果是先通知,等有時間再回主題拉 (pull) 資料呢?勢必 Update 就要傳入不同主題類別的指標,因此引進 Subject 抽象基礎類別就有必要。
class ISubject{public: virtual ~ISubject() = 0 {}public: virtual void Subscribe(const std::shared_ptr<IObserver>& ob) = 0; virtual void Unsubscribe(const std::shared_ptr<IObserver>& ob) = 0; virtual void Notify() = 0;public: virtual std::string GetStockID() const = 0; virtual double GetPrice() const = 0;};
// 台積電class CTSMCStockSubject : public ISubject{ // 其他程式碼省略...public: virtual void Notify() override { for (auto& ob : m_observers) { ob->Update(this); } }public: virtual std::string GetStockID() const override { return "2330.TW"; } virtual double GetPrice() const override { return 300; }private: std::set<std::shared_ptr<IObserver>> m_observers;};
// 鴻海class CFoxconnStockSubject : public ISubject{ // 其他程式碼省略...public: virtual std::string GetStockID() const override { return "2317.TW"; } virtual double GetPrice() const override { return 70; }};
Update 改為接收 ISubject*,其他參數都可以省略。
class IObserver{public: virtual ~IObserver() = 0 {}public: virtual void Update(ISubject* pSubject) = 0;};
// 技術分析class CTAViewObserver : public IObserver{public: virtual void Update(ISubject* pSubject) override { // 這裡可以先將 pSubject 儲存,稍後有時間再回主題類別拉 (pull) 資料 m_mapStockID2Subject[pSubject->GetStockID()] = pSubject; }private: std::map<std::string, ISubject*> m_mapStockID2Subject;};
到此為止說明了需要 Subject 抽象基礎類別的原因,下一篇會對設計做更深入的探討。


留言
張貼留言