問題:
在某些情況下,
一類具有“容器特徵”的對象在充當對象的同時,又是其他對象的容器的情況,比如樹狀結構的對象,如果客戶過多地依賴於對象容器複雜的內部實現結構,對象容器內部實現結構的變化將引起客戶代碼的頻繁變化。如何將“客戶代碼與複雜的對象容器結構”解耦?讓對象容器自己來實現自身的複雜結構,從而使得客戶代碼就像處理簡單對象一樣來處理複雜的對象容器?
定義:
組合(Composite )模式是構造型的設計模式之一,是指將對象組合成樹形結構以表示“部分-整體”的階層,組合模式使得客戶對單個對象和組合對象的使用具有一致性。
意圖:
提供一個Component角色,它定義了訪問與管理組件(部分-整體)的通用介面一般包含Add(),Remove()等方法。定義一個使用者儲存子組件的Composite角色,他實現了Component角色提供訪問與管理組件的所有方法。並維護了一個Component角色的引用列表,用於管理維護所有的子組件。這樣使得基礎對象可以被組合成更複雜的組合對象,而這個組合對象又可以被組合,這樣不斷地遞迴下去就形成了一個樹形結構的複雜物件。組合模式就是採用樹形結構來實現普遍存在的對象容器,從而將一對多的關係轉化一對一的關係,使得客戶代碼可以一致地處理對象和對象容器,無需關心處理的是單個的對象,還是組合的對象容器。將客戶程式與對象內部實現解耦,讓對象容器自己來實現自身的複雜結構,解耦後,客戶程式只能對象上層的介面發生關係。
參與者:
•抽象組件角色(Component):
是組合中的對象聲明介面,在適當的情況下,實現所有類共有介面的預設行為。聲明一個介面用於訪問和管理Component子組件。
•葉組件角色(Leaf):
在組合中表示分葉節點對象沒有子節點,實現抽象組件角色聲明的介面。
•子組件角色(Composite):
在組合中表示分支節點對象,用來儲存子組件,有子節點,實現抽象組件角色聲明的介面如增加(add)和刪除(remove)等。
UML:
代碼說明:
/// <summary>
/// 抽象組件角色(Component)
/// </summary>
public abstract class Component
{
public string Name { get; set; }
public abstract void Add(Component component);
public abstract void Remove(Component component);
public abstract void Show();
}
/// <summary>
/// 葉組件角色(Leaf)
/// </summary>
public class Leaf : Component
{
public override void Add(Component component)
{
System.Console.WriteLine("我是一個葉子節點,不能添加子物件");
}
public override void Remove(Component component)
{
System.Console.WriteLine("我是一個葉子節點,沒有子物件");
}
public override void Show()
{
System.Console.WriteLine("我是一個葉子節點。");
}
}
/// <summary>
/// 子組件角色(Composite)
/// </summary>
public class CompositeClass : Component
{
List<Component> list = new List<Component>();
public override void Add(Component component)
{
list.Add(component);
}
public override void Remove(Component component)
{
list.Remove(component);
}
public override void Show()
{
foreach (var s in list)
{
System.Console.WriteLine(String.Format("我是{0}節點。",s.Name));
}
}
}
/// <summary>
/// 用戶端測試
/// </summary>
public void CompositeTest()
{
Component obj = new CompositeClass();
obj.Name = "我是個節點";
Component obj1 = new CompositeClass() { Name = "我還是一個節點" };
Component obj2 = new CompositeClass() { Name = "我還是一個節點" };
obj2.Add(new Leaf() { Name="我是一個葉子節點" });
obj.Add(obj1);
obj.Add(obj2);
}
優點:
•客戶代碼與複雜的對象容器結構解耦,讓對象容器自己來實現自身的複雜結構。添加或移除對象不必修改用戶端代碼。
•單個對象和組合對象具有一致性介面,所以使用者不必關係自己處理的是單個對象還是整個組合結構,這就簡化了用戶端代碼。
適用場合:
•表示對象的部分-整體階層(樹形結構)。
•希望使用者忽略組合對象與單個對象的不同,使用者將統一地使用組合結構中的所有對象。
透明方式與安全方式:
透明方式(用戶端不關係,不知道使用的是Composite還是Leaf):
在Component中聲明所有用來來管理子物件的方法,包含Add(),Remove()等,這樣喜歡Component介面所有的子物件都具備Add(),Remove()等,這樣能達到GoF所說的“單個對象和組合對象的使用具有一致性”可是對於Leaf節點不具備Add(),Remove()等操作,這個實現是沒有意義的。
安全方式:
在Composite中聲明所有用來來管理子物件的方法,不在Leaf中定義和實現不需要的操作,這樣會使Composite與Leaf不具有相同的介面,用戶端調用需要做相應的判斷。
GoF比較傾向於:以不遵守單一責任原則換取透明性,讓Client將組合和分葉節點具有一致性。
在實現組合模式時,有很多設計上的折衷。要根據需求平衡透明性和安全性。