C# 設(shè)計(jì)模式系列教程-組合模式
1. 概述
將對(duì)象組合成樹(shù)形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu)。組合模式使得用戶對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性。
2. 解決的問(wèn)題
當(dāng)希望忽略單個(gè)對(duì)象和組合對(duì)象的區(qū)別,統(tǒng)一使用組合結(jié)構(gòu)中的所有對(duì)象(將這種“統(tǒng)一”性封裝起來(lái))。
3. 組合模式中的角色
3.1 組合部件(Component):它是一個(gè)抽象角色,為要組合的對(duì)象提供統(tǒng)一的接口。
3.2 葉子(Leaf):在組合中表示子節(jié)點(diǎn)對(duì)象,葉子節(jié)點(diǎn)不能有子節(jié)點(diǎn)。
3.3 合成部件(Composite):定義有枝節(jié)點(diǎn)的行為,用來(lái)存儲(chǔ)部件,實(shí)現(xiàn)在Component接口中的有關(guān)操作,如增加(Add)和刪除(Remove)。
4. 模式解讀
4.1 組合模式的類圖

4.2 組合模式的實(shí)現(xiàn)代碼
/// <summary>
/// 一個(gè)抽象構(gòu)件,聲明一個(gè)接口用于訪問(wèn)和管理Component的子部件
/// </summary>
public abstract class Component
{
protected string name;
public Component(string name)
{
this.name = name;
}
/// <summary>
/// 增加一個(gè)節(jié)點(diǎn)
/// </summary>
/// <param name="component"></param>
public abstract void Add(Component component);
/// <summary>
/// 移除一個(gè)節(jié)點(diǎn)
/// </summary>
/// <param name="component"></param>
public abstract void Remove(Component component);
/// <summary>
/// 顯示層級(jí)結(jié)構(gòu)
/// </summary>
public abstract void Display(int level);
}
/// <summary>
/// 葉子節(jié)點(diǎn)
/// </summary>
public class Leaf : Component
{
public Leaf(string name)
: base(name)
{ }
/// <summary>
/// 由于葉子節(jié)點(diǎn)沒(méi)有子節(jié)點(diǎn),所以Add和Remove方法對(duì)它來(lái)說(shuō)沒(méi)有意義,但它繼承自Component,這樣做可以消除葉節(jié)點(diǎn)和枝節(jié)點(diǎn)對(duì)象在抽象層次的區(qū)別,它們具備完全一致的接口。
/// </summary>
/// <param name="component"></param>
public override void Add(Component component)
{
Console.WriteLine("Can not add a component to a leaf.");
}
/// <summary>
/// 實(shí)現(xiàn)它沒(méi)有意義,只是提供了一個(gè)一致的調(diào)用接口
/// </summary>
/// <param name="component"></param>
public override void Remove(Component component)
{
Console.WriteLine("Can not remove a component to a leaf.");
}
public override void Display(int level)
{
Console.WriteLine(new string('-',level) + name);
}
}
/// <summary>
/// 定義有枝節(jié)點(diǎn)的行為,用來(lái)存儲(chǔ)部件,實(shí)現(xiàn)在Component接口中對(duì)子部件有關(guān)的操作
/// </summary>
public class Composite : Component
{
public Composite(string name)
: base(name)
{ }
/// <summary>
/// 一個(gè)子對(duì)象集合,用來(lái)存儲(chǔ)其下屬的枝節(jié)點(diǎn)和葉節(jié)點(diǎn)
/// </summary>
private List<Component> children = new List<Component>();
/// <summary>
/// 增加子節(jié)點(diǎn)
/// </summary>
/// <param name="component"></param>
public override void Add(Component component)
{
children.Add(component);
}
/// <summary>
/// 移除子節(jié)點(diǎn)
/// </summary>
/// <param name="component"></param>
public override void Remove(Component component)
{
children.Remove(component);
}
public override void Display(int level)
{
Console.WriteLine(new string('-', level) + name);
// 遍歷其子節(jié)點(diǎn)并顯示
foreach (Component component in children)
{
component.Display(level+2);
}
}
}
4.3 客戶端代碼
class Program
{
static void Main(string[] args)
{
// 生成樹(shù)根,并為其增加兩個(gè)葉子節(jié)點(diǎn)
Component root = new Composite("Root");
root.Add(new Leaf("Leaf A in Root"));
root.Add(new Leaf("Leaf B in Root"));
// 為根增加兩個(gè)枝節(jié)點(diǎn)
Component branchX = new Composite("Branch X in Root");
Component branchY = new Composite("Branch Y in Root");
root.Add(branchX);
root.Add(branchY);
// 為BranchX增加頁(yè)節(jié)點(diǎn)
branchX.Add(new Leaf("Leaf A in Branch X"));
// 為BranchX增加枝節(jié)點(diǎn)
Component branchZ = new Composite("Branch Z in Branch X");
branchX.Add(branchZ);
// 為BranchY增加葉節(jié)點(diǎn)
branchY.Add(new Leaf("Leaf in Branch Y"));
// 為BranchZ增加葉節(jié)點(diǎn)
branchZ.Add(new Leaf("Leaf in Branch Z"));
// 顯示樹(shù)
root.Display(1);
Console.Read();
}
}
運(yùn)行結(jié)果

5. 透明方式與安全方式
5.1 透明方式:在Component中聲明所有來(lái)管理子對(duì)象的方法,其中包括Add,Remove等。這樣實(shí)現(xiàn)Component接口的所有子類都具備了Add和Remove方法。這樣做的好處是葉節(jié)點(diǎn)和枝節(jié)點(diǎn)對(duì)于外界沒(méi)有區(qū)別,它們具備完全一致的接口。
5.2 安全方式:在Component中不去聲明Add和Remove方法,那么子類的Leaf就不需要實(shí)現(xiàn)它,而是在Composit聲明所有用來(lái)管理子類對(duì)象的方法。
5.3 兩種方式有缺點(diǎn):對(duì)于透明方式,客戶端對(duì)葉節(jié)點(diǎn)和枝節(jié)點(diǎn)是一致的,但葉節(jié)點(diǎn)并不具備Add和Remove的功能,因而對(duì)它們的實(shí)現(xiàn)是沒(méi)有意義的;對(duì)于安全方式,葉節(jié)點(diǎn)無(wú)需在實(shí)現(xiàn)Add與Remove這樣的方法,但是對(duì)于客戶端來(lái)說(shuō),必須對(duì)葉節(jié)點(diǎn)和枝節(jié)點(diǎn)進(jìn)行判定,為客戶端的使用帶來(lái)不便。
6. 模式總結(jié)
6.1 優(yōu)點(diǎn)
6.1.1 使客戶端調(diào)用簡(jiǎn)單,它可以一致使用組合結(jié)構(gòu)或是其中單個(gè)對(duì)象,簡(jiǎn)化了客戶端代碼。
6.1.2 容易在組合體內(nèi)增加對(duì)象部件??蛻舳瞬槐匾蚣尤肓诵碌牟考拇a。有利于功能的擴(kuò)展。
6.2 缺點(diǎn)
6.2.1 需要抉擇使用透明方式還是安全方式。
6.2.2 透明方式違背了面向?qū)ο蟮膯我宦氊?zé)原則;安全方式增加了客戶需要端判定的負(fù)擔(dān)。
6.3 適用場(chǎng)景
6.3.1 當(dāng)想表達(dá)對(duì)象的部分-整體的層次結(jié)構(gòu)時(shí)
6.3.3 希望用戶忽略組合對(duì)象與單個(gè)對(duì)象的不同,用戶將統(tǒng)一地使用組合結(jié)構(gòu)中的所有對(duì)象時(shí)。
相關(guān)文章
C#中38個(gè)常用運(yùn)算符的優(yōu)先級(jí)的劃分和理解
這只我自己在學(xué)C#中的一些總結(jié),其中對(duì)于各級(jí)的劃分方式、各操作符的優(yōu)先級(jí)的理解并不見(jiàn)得正確,只是自己的看法,拿出來(lái)與大家分享2012-08-08
C#自定義控件實(shí)現(xiàn)TextBox禁止粘貼的方法
這篇文章主要介紹了C#自定義控件實(shí)現(xiàn)TextBox禁止粘貼的方法,結(jié)合具體實(shí)例形式分析了C#自定義控件的創(chuàng)建、使用方法及TextBox禁止粘貼的實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-06-06
C# 9 中新加入的關(guān)鍵詞 init,record,with
這篇文章主要介紹了C# 9 中新加入的關(guān)鍵詞 init,record,with的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)c# 9,感興趣的朋友可以了解下2020-08-08
Unity UGUI的Scrollbar滾動(dòng)條組件使用詳解
這篇文章主要介紹了Unity UGUI的Scrollbar(滾動(dòng)條)組件的介紹及使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07
c#遠(yuǎn)程html數(shù)據(jù)抓取實(shí)例分享
這篇文章主要介紹了c#遠(yuǎn)程html數(shù)據(jù)抓取的方法,大家參考使用吧2013-12-12

