2010-07-01 24 views
3

在我的應用程序中,我有很多類。這些類中的大多數存儲了相當多的數據,並且如果其中一個數據類的內容發生更改,我的應用程序中的其他模塊也會「更新」。觀察數據變化的不同方式

做到這一點,典型的方式是這樣的:

void MyDataClass::setMember(double d) 
{ 
m_member = d; 
notifyAllObservers(); 
} 

這是一個相當不錯的方法,如果會員不經常被更改和「觀察班的需求將上升,最新快儘可能。

觀察變化的另一種方式是這樣的:

void MyDataClass::setMember(double d) 
{ 
setDirty(); 
m_member = d; 
} 

這是如果成員發生了多次改變一個很好的方法,和「觀察班」看定期在所有的「髒」的情況。

不幸的是,我在我的類中混合了兩種數據成員。有些改變不是那麼頻繁(我可以和普通的觀察者一起生活),其他改變很多次(這是在複雜的數學算法中),每次調用觀察者都會改變我的應用程序的性能。

是否有任何其他的觀察數據更改的技巧,或者您可以輕鬆地將幾種不同的觀察數據更改的方法組合起來的模式?

雖然這是一個相當語言無關的問題(我可以嘗試理解其他語言的示例),但最終的解決方案應該可以在C++中使用。

回答

4

你所描述的兩種方法(概念上)涵蓋了兩個方面,但是我認爲你沒有充分解釋它們的優點和缺點。

有一件事你應該知道,它是人口因素。

  • Push方法是偉大的,當有很多通告程序和一些觀察家
  • 如果只有少數通知器與許多觀察家

如果您有許多通報人

  • 拉法是偉大的,你的觀察應該迭代他們每一個發現2或3是dirty ...它不會工作。另一方面,如果你有很多觀察者,並且每次更新都需要通知他們所有人,那麼你可能註定會失敗,因爲簡單地遍歷所有這些觀察者都會導致你的表現。

    然而,有一種可能性,你還沒有談到:然而,兩種方法相結合,另一種間​​接方式。

    • 按每次更改一個GlobalObserver
    • 擁有的GlobalObserver每一個觀看入住時需

    這不是那麼容易做的時候,因爲每一個觀看需要記住,當是最後一次檢查,只有在尚未觀察到的變化時才能得到通知。通常的技巧是使用時代。

    Epoch 0  Epoch 1  Epoch 2 
    event1  event2  ... 
    ...   ... 
    

    每個觀察者記得它需要讀取(當觀察者訂閱它被賦予的回報當前時期),下一個時代,從這個時期讀取到當前一個知道所有的事件。一般來說,當前時期不能由通知者訪問,例如,您可以決定每次讀取請求到達時(如果當前時期不爲空),則切換時期。

    這裏的難處是知道何時放棄時代(當他們不再需要時)。這需要某種引用計數。請記住GlobalObserver是將當前時代返回到對象的人。因此,我們爲每個時代引入一個計數器,它僅僅計算了有多少觀察者沒有觀察到這個時代(以及後來的)。

    • 在訂閱,我們回到時期數量和增加這個時代
    • 投票日的櫃檯,我們遞減接受調查的劃時代的計數器並返回當前時期數量並增加其計數器
    • 在退訂,我們減少時代的計數器 - >確保析構函數取消訂閱!

    它也可以將它與超時相結合,註冊我們最後一次修改的時期(即未來的創造),並決定該一定的時間後,我們就可以放棄它(在這種情況下,我們回收該計數器並將其添加到下一個時期)。

    注意,該方案擴展到多線程,因爲一個時代是用於編寫(堆棧上推操作)訪問,其餘是隻讀的(除了原子計數器)。它可以使用無鎖操作推堆在那個沒有內存需要分配的條件。當堆棧完成時決定切換時期是完全理智的。

  • +0

    令人印象深刻。 +1。 – Patrick 2010-07-02 08:25:17

    +0

    同一主題(帕特里克)新問題:http://stackoverflow.com/questions/3667317/best-way-to-keep-the-user-interface-up-to-date在那裏,他的言論,簡單地生成很多事件可能會導致表演失敗。 – 2010-09-08 13:35:23

    0

    您描述了可用的兩個高級選項(push vs pull/polling)。沒有我知道的其他選項。觀測數據的

    4

    其他的技巧改變

    不是真的。你有「推」和「拉」設計模式。沒有其他選擇。

    一個notifyAllObservers,普通屬性的訪問是一個

    我建議一致性。顯然,你有一種情況,一個對象有很多變化,但所有變化不滲透到其他對象。

    不要被這個困惑。

    觀察者不需要做一個昂貴的計算,僅僅是因爲它被通知了一個改變。

    我認爲你應該有這樣的類來處理「頻繁變化但請求緩慢」的類。

    class PeriodicObserver { 
        bool dirty; 
        public void notification(...) { 
         // save the changed value; do nothing more. Speed matters. 
         this.dirty= True; 
        } 
        public result getMyValue() { 
         if(this.dirty) { 
          // recompute now 
         } 
         return the value 
    } 
    
    2

    你有拉和推通知。我會考慮要隱瞞細節儘可能,所以至少在通知並不需要關心的區別:

    class notifier { 
    public: 
        virtual void operator()() = 0; 
    }; 
    
    class pull_notifier : public notifier { 
        bool dirty; 
    public: 
        lazy_notifier() : dirty(false) {} 
        void operator()() { dirty = true; } 
        operator bool() { return dirty; } 
    }; 
    
    class push_notifier : public notifier { 
        void (*callback)(); 
    public: 
        push_notifier(void (*c)()) : callback(c) {} 
        void operator()() { callback(); } 
    }; 
    

    然後觀察者可以通過其中一個push_notifierpull_notifier,因爲它認爲適應和轉變器並不需要關心的區別:

    class MyDataClass { 
        notifier &notify; 
        double m_member; 
    public: 
        MyDataClass(notifier &n) : n_(n) {} 
        void SetMember(double d) { 
         m_member = d; 
         notify(); 
        } 
    }; 
    

    對於我只有每一個突變單站寫它的那一刻,但它是相當簡單的改變,以指針的向量如果你需要更多的話,可以用於給定增變器的觀察者對象。由此,給定的增變器將支持push_和pull_通知器的任意組合。如果您確定給定的增變器只會使用pull_notifier或push_notifier,您可以考慮使用模板參數(策略)作爲模板參數,以避免虛函數調用的開銷(可能忽略push_notifier,但是對於pull_notifier而言更少)。