重學設計模式 - Composite(組合)模式 - 結構型

1、意圖

將對象組合成樹形結構以表示“部分 - 整體”的層次結構,Composite使得用戶對單個對象和組合對象的使用具有一致性。

2、動機

在圖形應用程序中,用戶可以使用簡單的圖元組件組合成較大的組件,這些組件又可以組合成更加複雜的組件。Composite描述瞭如何使用遞歸組合,使得用戶不必對這些類進行區別。它的關鍵是一個抽象類,既可以代表簡單的圖元,又可以代表圖元的組合。

3、適用性

  1. 想表示對象的“部分 - 整體”的層次結構
  2. 希望用戶忽略組合對象和單個對象的不同,用戶將統一使用組合結構中的所有對象。

4、結構

 

5、參與者

  • Component:
    - 爲組合中的對象聲明接口;
    - 在適當的情況下,實現所有類公共接口的缺省行爲;
    - 聲明一個接口用於訪問和管理Component的子組件;
    - (可選)在遞歸結構中定義一個接口,用於訪問一個父部件,並在合適的情況下實現它。
  • Leaf:
    - 在組合中表示葉節點對象,葉節點沒有子節點;
    - 在組合中定義其行爲。
  • Composite:
    - 定義組合部件的行爲;
    - 存儲子部件;
    - 實現Component接口中與子部件有關的操作。
  • Client:
    - 通過Component接口操縱所有部件對象。

6、協作

用戶使用Component類接口與組合結構中的對象進行交互。如果接收者是一個Leaf,則直接處理請求。如果接收者是Composite,它通常將請求發送給它的子部件,在轉發請求之前或/與之後可能執行一些輔助操作。

7、效果

  • 定義了包含基本對象和組合對象的類層次結構。客戶端代碼中,任何用到基本對象的地方都可以用組合對象。
  • 簡化客戶端代碼。客戶可以一致的使用組合對象和單個對象。
  • 更容易增加新類型的組件。
  • 使你的設計變得更加一般化。如果你希望一個組合只能有某些特定的組件。使用Composite時,你不能依賴類型系統施加這些約束,而必須在運行時刻進行檢查。

8、實現

  1. 顯式的父部件引用。保持“從子部件到父部件的引用”能簡化組合結構的遍歷和管理。父部件引用可以簡化結構的上移和組件的刪除,同時父部件引用也支持Chain of Responsibility模式。
    通常在Component類中定義父部件引用。Leaf和Composite類可以繼承這個引用以及管理這個引用的那些操作。
    對於父部件的引用,必須維護一個不變式,即一個組合的所有子節點以這個組合爲父節點,反之該組合以這些節點爲子節點。最容易的辦法是,僅當在一個組合中增加或刪除一個組件時,才改變這個組件的父部件。
    一句話:在Component基類中增加一個父部件指針,當操作Composite中的Add和Remove時,改變該指針。
  2. 共享組件。共享組件是很有用的,比如可以減少對存儲的需求。但是當一個組件只有一個父部件時,很難共享組件。
    一個可行的解決辦法是爲子部件存儲多個父部件,但當一個請求在結構中向上傳遞時,這種方法對導致多義性。Flyweight模式討論瞭如何修改設計以避免將父部件存儲在一起。另外,如果子部件可以將一些狀態(或所有狀態)存儲在外部,從而不需要向父部件發送請求,那麼這種方法是可行的。
  3. 最大化Component接口。對於僅對Composite類有意義的操作,定義一個某人的缺省實現,Leaf使用缺省實現,Composite重新實現這些操作。
  4. 聲明管理子部件的操作。問題:是在Component中聲明Add和Remove操作並使之對Leaf類有意義,還是隻在Composite中聲明並定義這些操作呢?前者具有很好的透明性,可以一致的使用所有的組件,但是客戶可能會對Leaf中執行Add和Remove操作;後者具有良好的安全性,但是使Leaf和Composite具有不同的接口,喪失了透明性。
    一種辦法是在Component中提供一個Composite* GetComposite()操作,來查詢當前組件是不是一個組合,根據返回值安全的執行Add和Remove操作。
    提供透明性的唯一方法是在Component定義缺省的Add和Remove操作。
    提供透明性的唯一方法是在Component中定義缺省的Add和Remove操作,使用缺省方式處理Add和Remove失敗(可能是產生一個異常)。Add失敗時,可能會造成內存泄露。
  5. Component是否應該實現一個Component列表。在基類中存放子類指針,對於葉節點來說會造成空間浪費,只有當結構中子類數目相對較少時,才值得使用這種方法。
  6. 子部件排序。如果需要考慮子節點的順序時,必須仔細地設計子節點的訪問和管理接口,以便管理子節點序列。參考Iterator模式。
  7. 使用高速緩衝存儲改善性能。如果需要對組合進行頻繁的遍歷和查找,Composite可以緩衝存儲對它的子節點進行遍歷或查找的相關信息。因此,此時需要定義一個接口來通知父部件它所緩衝存儲的信息無效或需要立即更新。
  8. 應該由誰刪除Component。在沒有垃圾回收機制的語言中,當一個Component被銷燬時,通常最好由Composite負責刪除其子節點。但有一種情況除外,即Leaf對象不會改變,因此可以被共享。
  9. 存儲組件最好使用那一種數據結構。

9、代碼示例

10、相關模式

  • 通常部件 - 父部件連接用於Chain of Responsibility模式
  • Decorator模式通常與Composite模式一起使用,它們通常有一個公共的父類。因此Decorator必須支持具有Add、Remove、GetChild等Component接口
  • Flyweight模式讓你共享組件,但不再能引用他們的父部件
  • Iterator模式用於遍歷Composite
  • Visitor模式將本來應該分佈在Composite和Leaf中的操作和行爲局部化。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章