還記得在觀察者模式一文中,IObserver::Update 最後修改為傳 ISubject*,因此 CTSMCStockSubject 在 Notify 的實作是傳入 this 指標,程式碼如下:
class IObserver{public: virtual void Update(ISubject* pSubject) = 0;};
class CTSMCStockSubject : public ISubject{public: virtual void Notify() override { for (auto& ob : m_observers) { ob->Update(this); // 傳入 this 指標 } }};
有沒有想過如果 CTAViewObserver 使用拉 (pull) 資料的方式,因此先將 ISubject* 儲存起來會有什麼問題呢?
class CTAViewObserver : public IObserver{public: virtual void Update(ISubject* pSubject) override { // 將 pSubject 儲存,稍後有時間再回主題類別拉 (pull) 資料 m_mapStockID2Subject[pSubject->GetStockID()] = pSubject; }};
答案就是,如果主題的生命週期短於觀察者,那麼到時觀察者存取 ISubject* 會導致未定義行為,因此我們會希望 Update 可以改為傳 std::shared_ptr< ISubject >,程式修改如下:
class IObserver{public: virtual void Update(const std::shared_ptr<ISubject>& pSubject) = 0;};
那麼現在 CTSMCStockSubject::Notify 要怎麼呼叫 Update 呢?也就是怎麼將 this 指標轉為 std::shared_ptr<ISubject> 呢?
這時就是 std::enable_shared_from_this 派上用場的地方了,首先 CTSMCStockSubject 要繼承至 std::enable_shared_from_this<CTSMCStockSubject>,再來呼叫 Update 的地方修改為傳入 shared_from_this(),程式碼如下:
class CTSMCStockSubject : public ISubject,public std::enable_shared_from_this<CTSMCStockSubject>
{public: virtual void Notify() override { for (auto& ob : m_observers) { ob->Update(shared_from_this()); } }};
最後要注意的地方是,現在 CTSMCStockSubject 必須要使用 std::shared_ptr 的方式才行。
int main(){ auto pTAViewObserver = std::make_shared<CTAViewObserver>(); auto pTSMCStockSubject = std::make_shared<CTSMCStockSubject>(); pTSMCStockSubject->Subscribe(pTAViewObserver); pTSMCStockSubject->Notify(); pTSMCStockSubject->Unsubscribe(pTAViewObserver); return 0;}
P.S. std::enable_shared_from_this 是個基底類別模板,模板參數是繼承的類別名稱,這個設計模式叫做 The Curiously Recurring Template Pattern (CRTP)。

留言
張貼留言