.Net結(jié)構(gòu)型設(shè)計(jì)模式之組合模式(Composite)
一、動(dòng)機(jī)(Motivate)
在我們的操作系統(tǒng)中有文件夾的概念,文件夾可以包含文件夾,可以嵌套多層,最里面包含的是文件,這個(gè)概念和“俄羅斯套娃”很像。當(dāng)然還有很多的例子,例如我們使用系統(tǒng)的時(shí)候,會(huì)使用到“系統(tǒng)菜單”,這個(gè)東西是樹(shù)形結(jié)構(gòu)。這些例子包含的這些東西或者說(shuō)是對(duì)象,可以分為兩類(lèi),一類(lèi)是:容器對(duì)象,可以包含其他的子對(duì)象;另一類(lèi)是:葉子對(duì)象,這類(lèi)對(duì)象是不能在包含其他對(duì)象的對(duì)象了。在軟件設(shè)計(jì)中,我們?cè)撛趺刺幚磉@種情況呢?是每類(lèi)對(duì)象分別對(duì)待,還是提供一個(gè)統(tǒng)一的操作方式呢。組合模式給我們提供了一種解決此類(lèi)問(wèn)題的一個(gè)途徑。
客戶(hù)代碼過(guò)多地依賴(lài)于對(duì)象容器(對(duì)象容器是對(duì)象的容器,細(xì)細(xì)評(píng)味)復(fù)雜的內(nèi)部實(shí)現(xiàn)結(jié)構(gòu),對(duì)象容器內(nèi)部實(shí)現(xiàn)結(jié)構(gòu)(而非抽象接口)的變化將引起客戶(hù)代碼的頻繁變化,帶來(lái)了代碼的維護(hù)性、擴(kuò)展性等方面的弊端。如何將“客戶(hù)代碼與復(fù)雜的對(duì)象容器結(jié)構(gòu)”解耦?如何讓對(duì)象容器自己來(lái)實(shí)現(xiàn)自身的復(fù)雜結(jié)構(gòu),從而使得客戶(hù)代碼就像處理簡(jiǎn)單對(duì)象一樣來(lái)處理復(fù)雜的對(duì)象容器?
二、意圖(Intent)
將對(duì)象組合成樹(shù)形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu)。Composite使得用戶(hù)對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性。 —— 《設(shè)計(jì)模式》GoF
三、結(jié)構(gòu)圖(Structure)
四、模式的組成
組合模式中涉及到三個(gè)角色:
(1)、抽象構(gòu)件角色(Component):這是一個(gè)抽象角色,它給參加組合的對(duì)象定義出了公共的接口及默認(rèn)行為,可以用來(lái)管理所有的子對(duì)象(在透明式的組合模式是這樣的)。在安全式的組合模式里,構(gòu)件角色并不定義出管理子對(duì)象的方法,這一定義由樹(shù)枝結(jié)構(gòu)對(duì)象給出。
(2)、樹(shù)葉構(gòu)件角色(Leaf):樹(shù)葉對(duì)象是沒(méi)有下級(jí)子對(duì)象的對(duì)象,定義出參加組合的原始對(duì)象的行為。(原始對(duì)象的行為可以理解為沒(méi)有容器對(duì)象管理子對(duì)象的方法,或者 【原始對(duì)象行為】+【管理子對(duì)象的行為(Add,Remove等)】=面對(duì)客戶(hù)代碼的接口行為集合)
(3)、樹(shù)枝構(gòu)件角色(Composite):代表參加組合的有下級(jí)子對(duì)象的對(duì)象,樹(shù)枝對(duì)象給出所有管理子對(duì)象的方法實(shí)現(xiàn),如Add、Remove等。
組合模式實(shí)現(xiàn)的最關(guān)鍵的地方是——簡(jiǎn)單對(duì)象和復(fù)合對(duì)象必須實(shí)現(xiàn)相同的接口。這就是組合模式能夠?qū)⒔M合對(duì)象和簡(jiǎn)單對(duì)象進(jìn)行一致處理的原因。
五、組合模式的具體代碼實(shí)現(xiàn)
組合模式有兩種實(shí)現(xiàn)方式,一種是:透明式的組合模式,另外一種是:安全式的組合模式
1、透明式的組合模式
指“抽象構(gòu)件角色”定義的接口行為集合包含兩個(gè)部分,一部分是葉子對(duì)象本身所包含的行為(比如Operation),另外一部分是容器對(duì)象本身所包含的管理子對(duì)象的行為(Add,Remove)。這個(gè)抽象構(gòu)件必須同時(shí)包含這兩類(lèi)對(duì)象所有的行為,客戶(hù)端代碼才會(huì)透明的使用,無(wú)論調(diào)用容器對(duì)象還是葉子對(duì)象,接口方法都是一樣的,這就是透明,針對(duì)客戶(hù)端代碼的透明。
/// <summary> /// 該抽象類(lèi)就是文件夾抽象接口的定義,該類(lèi)型就相當(dāng)于是抽象構(gòu)件Component類(lèi)型 /// </summary> public abstract class Folder { public abstract void Add(Folder folder);//增加文件夾或文件 public abstract void Remove(Folder folder);//刪除文件夾或者文件 public abstract void Open(); //打開(kāi)文件或者文件夾--該操作相當(dāng)于Component類(lèi)型的Operation方法 } /// <summary> /// 該Word文檔類(lèi)就是葉子構(gòu)件的定義,該類(lèi)型就相當(dāng)于是Leaf類(lèi)型,不能在包含子對(duì)象 /// </summary> public sealed class Word : Folder { public override void Add(Folder folder)//增加文件夾或文件 { throw new Exception("Word文檔不具有該功能"); } public override void Remove(Folder folder)//刪除文件夾或者文件 { throw new Exception("Word文檔不具有該功能"); } public override void Open()//打開(kāi)文件--該操作相當(dāng)于Component類(lèi)型的Operation方法 { Console.WriteLine("打開(kāi)Word文檔,開(kāi)始進(jìn)行編輯"); } } /// <summary> /// SonFolder類(lèi)型就是樹(shù)枝構(gòu)件,由于我們使用的是“透明式”,所以Add,Remove都是從Folder類(lèi)型繼承下來(lái)的 /// </summary> public class SonFolder : Folder { public override void Add(Folder folder)//增加文件夾或文件 { Console.WriteLine("文件或者文件夾已經(jīng)增加成功"); } public override void Remove(Folder folder)//刪除文件夾或者文件 { Console.WriteLine("文件或者文件夾已經(jīng)刪除成功"); } public override void Open()//打開(kāi)文件夾--該操作相當(dāng)于Component類(lèi)型的Operation方法 { Console.WriteLine("已經(jīng)打開(kāi)當(dāng)前文件夾"); } } public class Program { static void Main() { Folder myword = new Word(); myword.Open();//打開(kāi)文件,處理文件 myword.Add(new SonFolder());//拋出異常 myword.Remove(new SonFolder());//拋出異常 Folder myfolder = new SonFolder(); myfolder.Open();//打開(kāi)文件夾 myfolder.Add(new SonFolder());//成功增加文件或者文件夾 myfolder.Remove(new SonFolder());//成功刪除文件或者文件夾 } }
2、安全式的組合模式
指“抽象構(gòu)件角色”只定義葉子對(duì)象的方法,確切的說(shuō)這個(gè)抽象構(gòu)件只定義兩類(lèi)對(duì)象共有的行為,然后容器對(duì)象的方法定義在“樹(shù)枝構(gòu)件角色”上,這樣葉子對(duì)象有葉子對(duì)象的方法,容器對(duì)象有容器對(duì)象的方法,這樣責(zé)任很明確,當(dāng)然調(diào)用肯定不會(huì)拋出異常了。
/// <summary> /// 該抽象類(lèi)就是文件夾抽象接口的定義,該類(lèi)型就相當(dāng)于是抽象構(gòu)件Component類(lèi)型 /// </summary> public abstract class Folder //該類(lèi)型少了容器對(duì)象管理子對(duì)象的方法的定義,換了地方,在樹(shù)枝構(gòu)件也就是SonFolder類(lèi)型 { public abstract void Open(); //打開(kāi)文件或者文件夾--該操作相當(dāng)于Component類(lèi)型的Operation方法 } /// <summary> /// 該Word文檔類(lèi)就是葉子構(gòu)件的定義,該類(lèi)型就相當(dāng)于是Leaf類(lèi)型,不能在包含子對(duì)象 /// </summary> public sealed class Word : Folder //這類(lèi)型現(xiàn)在很干凈 { public override void Open() //打開(kāi)文件--該操作相當(dāng)于Component類(lèi)型的Operation方法 { Console.WriteLine("打開(kāi)Word文檔,開(kāi)始進(jìn)行編輯"); } } /// <summary> /// SonFolder類(lèi)型就是樹(shù)枝構(gòu)件,現(xiàn)在由于我們使用的是“安全式”,所以Add,Remove都是從此處開(kāi)始定義的 /// </summary> public abstract class SonFolder : Folder //這里可以是抽象接口,可以自己根據(jù)自己的情況而定 { public abstract void Add(Folder folder); //增加文件夾或文件 public abstract void Remove(Folder folder); //刪除文件夾或者文件 public override void Open()//打開(kāi)文件夾--該操作相當(dāng)于Component類(lèi)型的Operation方法 { Console.WriteLine("已經(jīng)打開(kāi)當(dāng)前文件夾"); } } /// <summary> /// NextFolder類(lèi)型就是樹(shù)枝構(gòu)件的實(shí)現(xiàn)類(lèi) /// </summary> public sealed class NextFolder : SonFolder { public override void Add(Folder folder)//增加文件夾或文件 { Console.WriteLine("文件或者文件夾已經(jīng)增加成功"); } public override void Remove(Folder folder) //刪除文件夾或者文件 { Console.WriteLine("文件或者文件夾已經(jīng)刪除成功"); } public override void Open()//打開(kāi)文件夾--該操作相當(dāng)于Component類(lèi)型的Operation方法 { Console.WriteLine("已經(jīng)打開(kāi)當(dāng)前文件夾"); } } public class Program { static void Main() { Folder myword = new Word();//這是安全的組合模式 myword.Open();//打開(kāi)文件,處理文件 Folder myfolder = new NextFolder(); myfolder.Open();//打開(kāi)文件夾 //此處要是用增加和刪除功能,需要轉(zhuǎn)型的操作,否則不能使用 ((SonFolder)myfolder).Add(new NextFolder());//成功增加文件或者文件夾 ((SonFolder)myfolder).Remove(new NextFolder());//成功刪除文件或者文件夾 } }
六、組合模式的實(shí)現(xiàn)要點(diǎn):
1、Composite模式采用樹(shù)形結(jié)構(gòu)來(lái)實(shí)現(xiàn)普遍存在的對(duì)象容器,從而將“一對(duì)多”的關(guān)系轉(zhuǎn)化為“一對(duì)一”的關(guān)系,使得客戶(hù)代碼可以一致地處理對(duì)象和對(duì)象容器,無(wú)需關(guān)心處理的是單個(gè)的對(duì)象,還是組合的對(duì)象容器。
2、將“客戶(hù)代碼與復(fù)雜的對(duì)象容器結(jié)構(gòu)”解耦是Composite模式的核心思想,解耦之后,客戶(hù)代碼將與純粹的抽象接口——而非對(duì)象容器的復(fù)雜內(nèi)部實(shí)現(xiàn)結(jié)構(gòu)——發(fā)生依賴(lài)關(guān)系,從而更能“應(yīng)對(duì)變化”。
3、Composite模式中,是將“Add和Remove等和對(duì)象容器相關(guān)的方法”定義在“表示抽象對(duì)象的Component類(lèi)”中,還是將其定義在“表示對(duì)象容器的Composite類(lèi)”中,是一個(gè)關(guān)乎“透明性”和“安全性”的兩難問(wèn)題,需要仔細(xì)權(quán)衡。這里有可能違背面向?qū)ο蟮?ldquo;單一職責(zé)原則”,但是對(duì)于這種特殊結(jié)構(gòu),這又是必須付出的代價(jià)。ASP.Net控件的實(shí)現(xiàn)在這方面為我們提供了一個(gè)很好的示范。
4、Composite模式在具體實(shí)現(xiàn)中,可以讓父對(duì)象中的子對(duì)象反向追朔;如果父對(duì)象有頻繁的遍歷需求,可使用緩存技巧來(lái)改善效率。
組合模式的優(yōu)點(diǎn):
(1)、組合模式使得客戶(hù)端代碼可以一致地處理對(duì)象和對(duì)象容器,無(wú)需關(guān)系處理的單個(gè)對(duì)象,還是組合的對(duì)象容器。
(2)、將”客戶(hù)代碼與復(fù)雜的對(duì)象容器結(jié)構(gòu)“解耦。
(3)、可以更容易地往組合對(duì)象中加入新的構(gòu)件。
組合模式的缺點(diǎn):
使得設(shè)計(jì)更加復(fù)雜??蛻?hù)端需要花更多時(shí)間理清類(lèi)之間的層次關(guān)系。(這個(gè)是幾乎所有設(shè)計(jì)模式所面臨的問(wèn)題)。
在以下情況下應(yīng)該考慮使用組合模式:
(1)、需要表示一個(gè)對(duì)象整體或部分的層次結(jié)構(gòu)。
(2)、希望用戶(hù)忽略組合對(duì)象與單個(gè)對(duì)象的不同,用戶(hù)將統(tǒng)一地使用組合結(jié)構(gòu)中的所有對(duì)象。
七、.NET 中組合模式的實(shí)現(xiàn)
其實(shí)組合模式在FCL里面運(yùn)用還是很多的,不知道大家是不是有所感覺(jué),這個(gè)模式大多數(shù)是運(yùn)用在控件上或者是和界面操作、展示相關(guān)的操作上。這個(gè)模式在.NET 中最典型的應(yīng)用就是應(yīng)用與WinForms和Web的開(kāi)發(fā)中,在.NET類(lèi)庫(kù)中,都為這兩個(gè)平臺(tái)提供了很多現(xiàn)有的控件,然而System.Windows.Forms.dll中System.Windows.Forms.Control類(lèi)就應(yīng)用了組合模式,因?yàn)榭丶↙abel、TextBox等這樣的簡(jiǎn)單控件,這些控件可以理解為葉子對(duì)象,同時(shí)也包括GroupBox、DataGrid這樣復(fù)合的控件或者叫容器控件,每個(gè)控件都需要調(diào)用OnPaint方法來(lái)進(jìn)行控件顯示,為了表示這種對(duì)象之間整體與部分的層次結(jié)構(gòu),微軟把Control類(lèi)的實(shí)現(xiàn)應(yīng)用了組合模式(確切地說(shuō)應(yīng)用了透明式的組合模式)。
到此這篇關(guān)于.Net結(jié)構(gòu)型設(shè)計(jì)模式之組合模式(Composite)的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C#列出局域網(wǎng)中可用SQL Server服務(wù)器(續(xù))
上一篇文章展示了使用COM對(duì)象如何列出局域網(wǎng)中的 SQL Server服務(wù)器信息,后來(lái)還發(fā)現(xiàn)在.Net中有現(xiàn)成的類(lèi)可用,而不需要使用不太熟悉的COM對(duì)象了,這樣豈不是更好?下面我把代碼展示給大家:2008-04-04.Net行為型設(shè)計(jì)模式之備忘錄模式(Memento)
這篇文章介紹了.Net行為型設(shè)計(jì)模式之備忘錄模式(Memento),文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05通過(guò)VS中的數(shù)據(jù)源選擇對(duì)話框簡(jiǎn)單實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接配置
通過(guò)VS中的數(shù)據(jù)源選擇對(duì)話框簡(jiǎn)單實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接配置...2007-02-02IIS7 應(yīng)用程序池的 托管管道模式與集成模式小結(jié)
而 IIS 7 完全整合 .NET 之后,架構(gòu)的處理順序有了很大的不同(如下圖),最主要的原因就是 ASP.NET 從 IIS 插件(ISAPI extension)的角色,進(jìn)入了 IIS 核心,而且也能以 ASP.NET 模塊負(fù)責(zé)處理 IIS 7 的諸多類(lèi)型要求。2011-02-02ASP.NET實(shí)現(xiàn)word文檔在線預(yù)覽功能代碼
文檔管理系統(tǒng)需要實(shí)現(xiàn)WORD能在線預(yù)覽功能,以前覺(jué)得挺難的,經(jīng)過(guò)一番研究發(fā)現(xiàn),WORD自帶的另存為可以保存為HTMl文件。2010-07-07