在上一篇我們推導出需要 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 抽象基礎類別的原因,下一篇會對設計做更深入的探討。