AD

Design Pattern - Observer

.introduction

        Observer Pattern,中文叫觀察者模式, 或者有人稱發布/訂閱模式, 用於單一物件對多個物件的情形下, 當此單一物件狀態改變時, 其他多個物件可以取得通知並做更新的動作.



.Thinking in Observer Pattern

        定義
            - Observer Pattern 為物件之間定義一對多的關係,當一個物件的狀態改變,其關係物件都會收到通知,並自動更新。

        在架構上, Observer包含主題物件以及觀察者, 兩者呈一對多的關係, 觀察者訂閱感興趣的主題, 一個主題由一到多個觀察者訂閱, 當主題物件(發行者)有狀態變化時, 會主動通知各個觀察者(訂閱者), 由觀察者去做更新的動作取得最新狀態, 並依各自需求去做相對應的處理.

        在需求使用上, Observer算是很常見的模型, 只要是一對多的架構就很常見到這個模型, 像是GUI架構的event處理就是一個很明顯的例子, 以下簡單舉個例子, 另外注意一點, 舉例並不代表一定要用這個模型去做處理, 也有其他模型可以達到相同的目的, 在設計時要以自己的需求跟狀況去做考量活用.

  • 建立很多視窗按鈕, 每個都註冊滑鼠訊息, 此時滑鼠的訊息處理器就是一個主題物件, 當滑鼠有狀態更新時, 就會通知每個有註冊的視窗按鈕, 當視窗按鈕收到消息, 前往取得滑鼠最新狀態後, 就可以依照各自的需求去做處理.

.implement

        Observer的主要原件有兩個, 實作上並不複雜, 只要把主題以及觀察者的角色定義弄清楚就可以, 以下列出幾個要點並做說明. 

  1. 定義出觀察者(以下稱Observer)的interface.
  2. 定義出主題(以下稱subject)的interface.
  3. 實做subject.
  4. 實做Observer.

.1.定義Observer的interface


       Observer功能沒有什麼特別之處, 定義固定的update功能即可. 

  • Update - 給subject在狀態改變時呼叫, 一旦被呼叫就去取得subject最新狀態來做相對應處理.
IObserver
class IObserver
{
    public:
        virtual void Update()=0;
};

.2.定義subject的interface


        網路上有些文章並沒有處理subject的interface, 而在此處為什麼要做subject的interface, 主要原因是subject主要功能包含了兩個部分, 其中狀態是屬於變動部分, 依各種需求在撰寫上也會不一樣, 而subject本身的註冊等基本功能是屬於不變動的功能, 所以理所當然把變動與不變動部分切分出來, 增加架構的彈性.

        雖說要把兩個部分切分出來, 但要分出哪個部分才是適當的, 合理的做法是把註冊等功能都非變動部分切分出來, 而變動的狀態部分依各種的需求實做, 日後有需要可較彈性的實作別種狀態的subject, 另外更進一步看, 非變動的註冊等功能, 程式碼很固定不會改變, 以abstract處理, 或者有其他考量下用interface然後再多一層, 端看個人怎麼設計都可以, 在這邊我以abstract class來處理.

  • ObserverCollection - 儲存已註冊的IObserver.
  • RegisterObserver - 註冊IObserver.
  • UnRegisterObserver - 註銷IObserver.
  • NotifyAllObservers - 當本身得知狀態改變時呼叫, 裡面呼叫所有IObserver的Update函式通知該取已更新的狀態了.

ISubject
class ISubject
{
    public:
        ISubject(){}
        ~ISubject(){}

        void RegisterObserver(IObserver *observer) 
        {
            //add observer
        }

        void UnRegisterObserver(IObserver *observer)
        {
            //remove observer
        }

        NotifyAllObservers()
        {
            //Call All Update in Observer...
            //這邊可以先確認每一個observer是有效的在執行, 
            //防範observer已經釋放卻沒有UnRegisterObserver
            //m_ObserverCollection[].Update();
        }
    public:
       //IObserver collection
       m_ObserverCollection;
};

.3.實做subject

        實作Subject主要就是加上自訂的狀態, 取狀態以及設定狀態都處理好, 另外不要忘了繼承(或實作)ISubject.

Subject
class Subject:public ISubject
{
    public:
        Subject(){ m_state = INIT_STATE;}
        ~Subject(){}

        MyState GetState()
        {
            return m_state;
        }

        void SetState(Mystate state)
        {
            m_state = state; 
        }

    public:
        MyState m_state;
};

.4.實做Observer

        Observer本身注意要重載Update函式, 以及記錄自己註冊了哪個Subject, 用於Update時可以呼叫來取狀態.

Observer
class Observer : public IObserver
{
    public:
        Observer(){}
        ~Observer(){}
        virtual void Update()
        {
            //get state
            MyState state = m_subject.GetState();
            
            //state processing....
        }
    public:
        Subject m_subject;
};


.Observer完整架構及執行


Observer
void main()
{
    Subject sub;
    Observer ob1,ob2;

    sub.RegisterObserver(&ob1);
    sub.RegisterObserver(&ob2);

    //......
    //途中Subject有更新時
    sub.SetState(STATE_ORZ);
    sub.NotifyAllObservers();
    //......

    sub.UnRegisterObserver(&ob1);
    sub.UnRegisterObserver(&ob2);
}

沒有留言:

張貼留言