2011-05-31 39 views
1
Observer *o = New Observer(); 
Subject *s = new Subject() ; 
s->register(o); 

//Is it a good practice to delete the observer in the unregister function? 
//I feel it is not. As the Observer object might still be in use, for example , 
//it might be registered to another Subject. 
s->unregister(o); 

//So it is safe to rely on the client code to delete the object or rely on the smart pointer things 
delete o; 

我想確認我的上述理解對於誰應該刪除觀察者對象是否正確。註銷時應該刪除觀察者嗎?

+1

想想你有觀察者觀察兩個不同主題的情況。除此之外,你在函數名稱中沒有提到的註銷函數中做的事情,所以你的函數被稱爲unregisterAndDelete,但是再一次,你有一個函數做兩個邏輯上分離的事情(註冊管理和資源管理),這是一個壞主意開始。 – LavaScornedOven 2011-05-31 11:23:31

+0

另一種想法是:刪除對象時最有可能取消註冊(除非主題知道註冊的觀察者被刪除)。如果你可以在註銷函數或觀察者的析構函數中滿足nothrow保證,那麼你可能應該在觀察者的析構函數中調用unregister,但是作爲缺點,你應該保留觀察者註冊到的所有主題的觀察者列表您可以遍歷主題列表並從每個主題中取消註冊您的觀察者)。 – LavaScornedOven 2011-05-31 16:45:57

回答

5

我同意你的觀察。這是不以刪除註銷功能觀察者一個很好的做法 - 一個簡單的事實是「一個誰創建資源必須負責刪除資源」

這將避免

  • 魔術行爲由創作者感知。
  • 代碼行爲將被很好地定義 - 創建者必須刪除。這將爲您的系統開發新開發人員奠定整體基礎。

在所有具有不同命名法的書籍的長度下討論了相似的主題。

4

我會說使用智能指針,因爲沒有必要記得明確地調用delete

+0

+1:使用智能指針。 – Puppy 2011-05-31 11:22:04

+2

準確地說,使用共享指針。這樣一旦觀察者沒有在任何地方註冊,觀察者就會被刪除。 – 2011-05-31 11:29:35

+0

@Tamas:我同意,因爲曾經我們確實擁有共享所有權,因此使用共享指針似乎已經足夠。 – 2011-05-31 11:48:47

2

從設計的角度來看,如果觀察者得到很好的實施,那麼註銷主體應該是多餘的(雖然沒有錯)。依賴外部代碼來確保強制執行的行爲始終可以被視爲糟糕的設計。

關於智能指針的使用,如果由於某種原因需要對銷燬點進行控制(例如,爲了避免在後面的代碼中存在訪問問題,在給定時間點關閉文件),那麼您應該明確地刪除,否則它依靠智能指針更加舒適。這就是爲什麼存在。

1

這取決於你的設計。我個人更喜歡刪除觀察者會自動將其與主題分離,即在析構函數中註銷自己。這節省了註銷註冊的麻煩,這就要求您在銷燬點時提及主題和觀察者。

2

由於Subject沒有分配Observer,所以不應該試圖釋放它。這允許Subject的客戶端以其選擇的任何方式(自定義分配器,靜態分配,自動變量)管理生命週期和分配策略Observer。它不強制客戶使用new

很顯然,客戶責任是不讓Observer在「未註冊」之前銷燬。

例如

Observer o; 
Subject s; 

s.register(&o); // could take a reference 

// ... 

s.unregister(&o); 

// No potential for forgotten deletes 
+0

+1對我來說,這是離開觀察者一個人的最好理由。由於客戶有責任註冊觀察員,因此明確對稱是註銷觀察員的責任。感謝您提及參考可以(我想說應該)被使用。 – daramarak 2011-06-01 07:49:08

+0

由於Observer在堆棧中創建,當它超出範圍時它將被銷燬,主體是否最終不會通知空對象? – pierrotlefou 2011-06-01 12:47:17

+0

@pierr:'Subject'也是一個局部變量,但是如果註銷不工作,它將首先超出範圍_even。 – 2011-06-01 16:10:56

0

智能指針或 如果客戶端代碼創建對象,讓相同的代碼來破壞它:)

0

反對刪除另一種說法客戶端代碼(如果可能的話不轉移所有權)是什麼主題對象溝通

通過刪除對象,您的Subject對象已獲得對象的所有權,因爲它控制着它的生命週期。由於原始指針被傳遞到對象中,而不是auto_ptr或unique_ptr,我將假定它是Subject的一個用戶,它不會在不查看代碼的情況下獲取對象的所有權。所以我會說Subject的接口不會傳達它擁有對象的所有權。因此,Subject不應刪除它,因爲其他人(可能)擁有它的所有權。

0

一般來說,您應該避免像刪除指向容器中的對象的指向的副作用,除非容器也負責創建指針。

Observer模式指針的問題比這個更深入一些。

觀察者內在地具有對主體的某種參考。如果該引用是直接引用(例如不是句柄),那麼如果該對象超出範圍或被刪除,則當Observer進行Notify(...)調用時,它將引發異常(或更糟糕)。因此,當您刪除主題時,您必須確保您從觀察者分離(...)。

忘記這麼做是一個問題,所以我喜歡將它構建到虛擬析構函數的基類中。這也意味着主體知道Observer(對它的引用),因此它可以調用Detach(...)。這使你與Observer類似的情況(即,如果它被刪除)。我發現最簡單的方法是讓觀察者實際上是一個帶有主題註冊特定通知的單身人士,而不僅僅是「我有一些狀態供你更新」。這與GoF第298頁第7項「明確指定利益修改」一致。此外,只是簡單明瞭就可以縮小更新領域。它還允許任何地方更容易地連接任何主題表單。

另一個需要考慮的情況是在通知(..)迭代期間刪除主題時。在Observer中,您有一個主題列表A,B和C.您可以更改狀態並在A上調用通知(...)。A刪除B.現在繼續調用B上的通知(...) ,這是不存在的。如果你在析構函數中使用Detach(...),B可能不再在列表中。但是如果你選擇迭代一個你開始使用的主題副本而不是實際列表(在那裏,那麼做),它可能仍然是。由於這個原因,您的Observer需要在迭代過程中處理Detach(...)調用Notify(...)。

我在博客here上發佈了一個這方面的例子。