還記得在觀察者模式一文中,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)。
留言
張貼留言