通俗易懂設計模式解析——組合模式
前言
今天介紹的是結構型設計模式中的第四個模式,也就是組合模式(Composite Pattern)。組合模式也好理解,就拿我們電腦的檔案及資料夾來說吧,這就是一個較好的組合模式的例子。一個目錄下面包含檔案及資料夾,資料夾下面也包含檔案或資料夾。在這樣一層層下來,我們可以想象。他似乎像極了那個樹狀圖。而組合模式是依據樹型結構來組合物件。用來表示部分—整體層次關係。
組合模式介紹
一、來由
在我們軟體系統開發中,會遇到簡單物件與複雜物件一起使用的情況,就好比剛剛說的檔案目錄一樣,可以包含檔案和資料夾,資料夾下面也可以包含檔案和資料夾。但是由於簡單物件和複雜物件在功能使用上還是有一定的區別的,可能會造成客戶端呼叫時較為麻煩。這時候就需要將簡單物件和複雜物件統一一致對待。然而組合模式也就是解決這一麻煩的。
二、意圖
將物件組合成樹形結構以表示"部分-整體"的層次結構。組合模式使得使用者對單個物件和組合物件的使用具有一致性。
三、案例圖
四、組合模式程式碼示例
看上面案例圖,可以發現組合模式一般包含以下部分:
抽象構件角色:這是一個抽象角色,它給參加組合的物件定義了公共的介面和行為,在透明式的組合模式中,包含了對所有子物件的管理。但是在安全式的組合模式中,這裡不定義管理子物件的方法,而是由樹枝構件定義給出。
樹葉構件:樹葉構件意味著沒有下級物件,定義了參加組合物件的原始行為。
樹枝構件:代表有下級物件(樹枝或樹葉都有可能),給出管理其子類的物件。
在組合模式中,細分為兩種形式。1、透明式組合模式。2、安全式組合模式。這裡我們可以看看何為透明式何為安全式:
透明式:
抽象構件角色定義公共的介面和行為,這裡呢就包括對本物件的操作也包含其子物件的操作的。但是樹葉構件物件沒有其子類。但是依然繼承其功能介面和行為。這裡在介面和行為上,無論呼叫樹枝構件還是樹葉構件都是一樣的。這就屬於透明式了。
安全式:
由上面透明式講的,樹葉構件也會包含操作自己和子物件的介面和行為,但是沒有其子物件怎麼辦呢?當然是可以空著不寫,但會空。但是萬一實現呼叫了呢?對吧,還是有一定的安全隱患的。那麼安全式也就是說抽象構件包含操作本身物件的介面和行為,那麼樹葉構件也就包含操作本身物件的介面和行為了。樹枝構件則實現操作自身物件的介面和行為的同時,還需要實現操作其子類的物件的介面和行為。
就按開始所講的檔案目錄的案例,我們通過程式碼一起看看,在程式碼中如何實現組合模式的,這樣也可以更方便快捷的瞭解記憶:
透明式:
namespace Composite_Pattern { /// <summary> /// 透明式組合模式 /// </summary> class CompositePattern { } #region 抽象構件角色——抽象檔案目錄============ public abstract class Files { /// <summary> /// 增加子物件 /// </summary> public abstract void Add(Files files = null, string Name = null); /// <summary> /// 刪除子物件 /// </summary> public abstract void Remove(Files files=null, string Name = null); /// <summary> /// 操作本身物件 /// </summary> public abstract void Open(string Name); } #endregion #region 樹葉構件——檔案型別======================== /// <summary> /// TXT文字 /// </summary> public sealed class BookTxt : Files { public override void Add(Files files=null, string Name = null) { throw new NotImplementedException("不存在新增子類操作"); } public override void Remove(Files files=null, string Name = null) { throw new NotImplementedException("不存在刪除子類操作"); } public override void Open(string Name) { Console.WriteLine($"開啟一個名為【{Name}】txt文字"); } } #endregion #region 樹枝構件——資料夾型別================= public class SubFiles : Files { public override void Add(Files files=null, string Name = null) { if (files != null) { Console.WriteLine($"新增一個名為【{Name}】的資料夾"); } else { Console.WriteLine($"新增一個名為【{Name}】的txt文字"); } } public override void Remove(Files files=null, string Name = null) { if (files != null ) { Console.WriteLine($"刪除一個名為【{Name}】的資料夾"); } else { Console.WriteLine($"刪除一個名為【{Name}】的txt文字"); } } public override void Open(string Name) { Console.WriteLine($"開啟當前名為【{Name}】資料夾資料夾"); } } #endregion }
class Program { static void Main(string[] args) { //操作樹葉本身檔案 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 抽象構件角色——抽象檔案目錄============ public abstract class Files { /// <summary> /// 操作本身物件 /// </summary> public abstract void Open(string Name); } #endregion #region 樹葉構件——檔案型別======================== /// <summary> /// TXT文字 /// </summary> public sealed class BookTxt : Files { public override void Open(string Name) { Console.WriteLine($"開啟一個名為【{Name}】txt文字"); } } #endregion #region 抽象樹枝構件——安全模式,開始定義子類物件操作介面和行為================= 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($"開啟當前名為【{Name}】資料夾"); } } #endregion #region 具體樹枝構件——具體實現類 public class AbSubFiles : SubFiles { public override void Add(Files files = null, string Name = null) { if (files != null) { Console.WriteLine($"新增一個名為【{Name}】的資料夾"); } else { Console.WriteLine($"新增一個名為【{Name}】的txt文字"); } } public override void Remove(Files files = null, string Name = null) { if (files != null) { Console.WriteLine($"刪除一個名為【{Name}】的資料夾"); } else { Console.WriteLine($"刪除一個名為【{Name}】的txt文字"); } } public override void Open(string Name) { Console.WriteLine($"開啟當前名為【{Name}】資料夾"); } } #endregion }
class Program1 { static void Main(string[] args) { //操作樹葉本身檔案 BookTxt bookTxt = new BookTxt(); bookTxt.Open("文字檔案一"); //新增資料夾 AbSubFiles subFiles = new AbSubFiles(); subFiles.Open("檔案一"); subFiles.Add(new AbSubFiles(), "檔案二"); //刪除檔案 subFiles.Remove(Name: "文字檔案二"); Console.ReadLine(); } }
使用場景及優缺點
一、使用場景
1、部分——整體的環境。例如樹型選單,檔案管理
2、使用者希望對簡單物件與複雜物件擁有一致的操作時
二、優點
1、組合模式使得處理簡單物件和複雜物件有一致的操作,無需關心處理的簡單物件還是複雜物件
2、更簡單快捷的加入新的節點
三、缺點
1、使得設計複雜,難於理清層次
2、在使用透明式的時候違背了介面分離原則,但是在使用安全式的時候又違背了依賴倒置原則
總結
到這裡組合模式就介紹完了。這裡需要提及的是在使用透明式組合模式時,樹葉構件繼承了操作子類的介面和行為,但是它並沒有子類。在介面分離原則中提到——客戶不應被強迫依賴它不使用的方法。所以這裡違背了其原則,但是都遵循了依賴倒置原則,依賴於抽象。在實現安全式組合模式時,在客戶端呼叫時依賴於具體實現,也就違背了依賴倒置原則,但是卻將樹葉操作與樹枝構件操作分離,符合介面分離原則。在實現組合模式中不同形式實現有不同的問題。這就需要根據我們實際情況去衡量該如何使用了。
只有經歷過地獄般的折磨,才有征服天堂的力量。只有流過血的手指才能彈出世間的絕唱。
C#設計模式系列目錄
歡迎大家掃描下方二維碼,和我一起踏上設計模式的闖關之路吧!