前言
今天介紹的是結(jié)構(gòu)型設(shè)計(jì)模式中的第四個(gè)模式,也就是組合模式(Composite
Pattern)。組合模式也好理解,就拿我們電腦的文件及文件夾來(lái)說(shuō)吧,這就是一個(gè)較好的組合模式的例子。一個(gè)目錄下面包含文件及文件夾,文件夾下面也包含文件或文件夾。在這樣一層層下來(lái),我們可以想象。他似乎像極了那個(gè)樹(shù)狀圖。而組合模式是依據(jù)樹(shù)型結(jié)構(gòu)來(lái)組合對(duì)象。用來(lái)表示部分—整體層次關(guān)系。
組合模式介紹
一、來(lái)由
在我們軟件系統(tǒng)開(kāi)發(fā)中,會(huì)遇到簡(jiǎn)單對(duì)象與復(fù)雜對(duì)象一起使用的情況,就好比剛剛說(shuō)的文件目錄一樣,可以包含文件和文件夾,文件夾下面也可以包含文件和文件夾。但是由于簡(jiǎn)單對(duì)象和復(fù)雜對(duì)象在功能使用上還是有一定的區(qū)別的,可能會(huì)造成客戶端調(diào)用時(shí)較為麻煩。這時(shí)候就需要將簡(jiǎn)單對(duì)象和復(fù)雜對(duì)象統(tǒng)一一致對(duì)待。然而組合模式也就是解決這一麻煩的。
二、意圖
將對(duì)象組合成樹(shù)形結(jié)構(gòu)以表示"部分-整體"的層次結(jié)構(gòu)。組合模式使得用戶對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性。
三、案例圖
?
?
?
?
四、組合模式代碼示例
看上面案例圖,可以發(fā)現(xiàn)組合模式一般包含以下部分:
抽象構(gòu)件角色:
這是一個(gè)抽象角色,它給參加組合的對(duì)象定義了公共的接口和行為,在透明式的組合模式中,包含了對(duì)所有子對(duì)象的管理。但是在安全式的組合模式中,這里不定義管理子對(duì)象的方法,而是由樹(shù)枝構(gòu)件定義給出。
樹(shù)葉構(gòu)件:樹(shù)葉構(gòu)件意味著沒(méi)有下級(jí)對(duì)象,定義了參加組合對(duì)象的原始行為。
樹(shù)枝構(gòu)件:代表有下級(jí)對(duì)象(樹(shù)枝或樹(shù)葉都有可能),給出管理其子類(lèi)的對(duì)象。
?
在組合模式中,細(xì)分為兩種形式。1、透明式組合模式。2、安全式組合模式。這里我們可以看看何為透明式何為安全式:
透明式:
抽象構(gòu)件角色定義公共的接口和行為,這里呢就包括對(duì)本對(duì)象的操作也包含其子對(duì)象的操作的。但是樹(shù)葉構(gòu)件對(duì)象沒(méi)有其子類(lèi)。但是依然繼承其功能接口和行為。這里在接口和行為上,無(wú)論調(diào)用樹(shù)枝構(gòu)件還是樹(shù)葉構(gòu)件都是一樣的。這就屬于透明式了。
安全式:
由上面透明式講的,樹(shù)葉構(gòu)件也會(huì)包含操作自己和子對(duì)象的接口和行為,但是沒(méi)有其子對(duì)象怎么辦呢?當(dāng)然是可以空著不寫(xiě),但會(huì)空。但是萬(wàn)一實(shí)現(xiàn)調(diào)用了呢?對(duì)吧,還是有一定的安全隱患的。那么安全式也就是說(shuō)抽象構(gòu)件包含操作本身對(duì)象的接口和行為,那么樹(shù)葉構(gòu)件也就包含操作本身對(duì)象的接口和行為了。樹(shù)枝構(gòu)件則實(shí)現(xiàn)操作自身對(duì)象的接口和行為的同時(shí),還需要實(shí)現(xiàn)操作其子類(lèi)的對(duì)象的接口和行為。
?
就按開(kāi)始所講的文件目錄的案例,我們通過(guò)代碼一起看看,在代碼中如何實(shí)現(xiàn)組合模式的,這樣也可以更方便快捷的了解記憶:
?透明式:
namespace Composite_Pattern { /// <summary> /// 透明式組合模式 /// </summary> class
CompositePattern { }#region 抽象構(gòu)件角色——抽象文件目錄============ public abstract class
Files {/// <summary> /// 增加子對(duì)象 /// </summary> public abstract void Add(Files
files =null, string Name = null); /// <summary> /// 刪除子對(duì)象 /// </summary> public
abstract void Remove(Files files=null, string Name = null); /// <summary> ///
操作本身對(duì)象/// </summary> public abstract void Open(string Name); } #endregion
#region 樹(shù)葉構(gòu)件——文件類(lèi)型======================== /// <summary> /// TXT文本 ///
</summary> public sealed class BookTxt : Files { public override void Add(Files
files=null, string Name = null) { throw new NotImplementedException("不存在添加子類(lèi)操作"
); }public override void Remove(Files files=null, string Name = null) { throw
new NotImplementedException("不存在刪除子類(lèi)操作"); } public override void Open(string
Name) { Console.WriteLine($"打開(kāi)一個(gè)名為【{Name}】txt文本"); } } #endregion #region
樹(shù)枝構(gòu)件——文件夾類(lèi)型=================public class SubFiles : Files { public override void
Add(Files files=null, string Name = null) { if (files != null) {
Console.WriteLine($"添加一個(gè)名為【{Name}】的文件夾"); } else { Console.WriteLine($"
添加一個(gè)名為【{Name}】的txt文本"); } } public override void Remove(Files files=null, string
Name =null) { if (files != null ) { Console.WriteLine($"刪除一個(gè)名為【{Name}】的文件夾"); }
else { Console.WriteLine($"刪除一個(gè)名為【{Name}】的txt文本"); } } public override void
Open(string Name) { Console.WriteLine($"打開(kāi)當(dāng)前名為【{Name}】文件夾文件夾"); } } #endregion }
class Program { static void Main(string[] args) { //操作樹(shù)葉本身文件 Files bookTxt = new
BookTxt(); bookTxt.Open("文本文件一"); //新增文件夾 Files subFiles = new SubFiles();
subFiles.Open("文件一"); subFiles.Add(new SubFiles(), "文件二"); //刪除文件
subFiles.Remove(Name:"文本文件二"); Console.ReadLine(); } }
?
?安全式:
namespace Composite_Pattern1 { /// <summary> /// 安全式組合模式 /// </summary> class
CompositePattern { }#region 抽象構(gòu)件角色——抽象文件目錄============ public abstract class
Files {/// <summary> /// 操作本身對(duì)象 /// </summary> public abstract void Open(string
Name); }#endregion #region 樹(shù)葉構(gòu)件——文件類(lèi)型======================== /// <summary> ///
TXT文本/// </summary> public sealed class BookTxt : Files { public override void
Open(string Name) { Console.WriteLine($"打開(kāi)一個(gè)名為【{Name}】txt文本"); } } #endregion
#region 抽象樹(shù)枝構(gòu)件——安全模式,開(kāi)始定義子類(lèi)對(duì)象操作接口和行為================= public abstract class
SubFiles : Files {public abstract void Add(Files files = null, string Name =
null); public abstract void Remove(Files files = null, string Name = null);
public override void Open(string Name) { Console.WriteLine($"打開(kāi)當(dāng)前名為【{Name}】文件夾"
); } }#endregion #region 具體樹(shù)枝構(gòu)件——具體實(shí)現(xiàn)類(lèi) public class AbSubFiles : SubFiles {
public override void Add(Files files = null, string Name = null) { if (files !=
null) { Console.WriteLine($"添加一個(gè)名為【{Name}】的文件夾"); } else { Console.WriteLine($"
添加一個(gè)名為【{Name}】的txt文本"); } } public override void Remove(Files files = null,
string Name = null) { if (files != null) { Console.WriteLine($"
刪除一個(gè)名為【{Name}】的文件夾"); } else { Console.WriteLine($"刪除一個(gè)名為【{Name}】的txt文本"); } }
public override void Open(string Name) { Console.WriteLine($"打開(kāi)當(dāng)前名為【{Name}】文件夾"
); } }#endregion }
?
class Program1 { static void Main(string[] args) { //操作樹(shù)葉本身文件 BookTxt bookTxt =
new BookTxt(); bookTxt.Open("文本文件一"); //新增文件夾 AbSubFiles subFiles = new
AbSubFiles(); subFiles.Open("文件一"); subFiles.Add(new AbSubFiles(), "文件二"); //
刪除文件 subFiles.Remove(Name: "文本文件二"); Console.ReadLine(); } }
?
使用場(chǎng)景及優(yōu)缺點(diǎn)
一、使用場(chǎng)景
1、部分——整體的環(huán)境。例如樹(shù)型菜單,文件管理
2、用戶希望對(duì)簡(jiǎn)單對(duì)象與復(fù)雜對(duì)象擁有一致的操作時(shí)
二、優(yōu)點(diǎn)
1、組合模式使得處理簡(jiǎn)單對(duì)象和復(fù)雜對(duì)象有一致的操作,無(wú)需關(guān)心處理的簡(jiǎn)單對(duì)象還是復(fù)雜對(duì)象
2、更簡(jiǎn)單快捷的加入新的節(jié)點(diǎn)
三、缺點(diǎn)
1、使得設(shè)計(jì)復(fù)雜,難于理清層次
2、在使用透明式的時(shí)候違背了接口分離原則,但是在使用安全式的時(shí)候又違背了依賴倒置原則
總結(jié)
到這里組合模式就介紹完了。這里需要提及的是在使用透明式組合模式時(shí),樹(shù)葉構(gòu)件繼承了操作子類(lèi)的接口和行為,但是它并沒(méi)有子類(lèi)。在接口分離原則中提到——客戶不應(yīng)被強(qiáng)迫依賴它不使用的方法。所以這里違背了其原則,但是都遵循了依賴倒置原則,依賴于抽象。在實(shí)現(xiàn)安全式組合模式時(shí),在客戶端調(diào)用時(shí)依賴于具體實(shí)現(xiàn),也就違背了依賴倒置原則,但是卻將樹(shù)葉操作與樹(shù)枝構(gòu)件操作分離,符合接口分離原則。在實(shí)現(xiàn)組合模式中不同形式實(shí)現(xiàn)有不同的問(wèn)題。這就需要根據(jù)我們實(shí)際情況去衡量該如何使用了。
只有經(jīng)歷過(guò)地獄般的折磨,才有征服天堂的力量。只有流過(guò)血的手指才能彈出世間的絕唱。
C#設(shè)計(jì)模式系列目錄 <https://www.cnblogs.com/hulizhong/p/11394686.html>
歡迎大家掃描下方二維碼,和我一起踏上設(shè)計(jì)模式的闖關(guān)之路吧!
熱門(mén)工具 換一換
