前言
今天介紹的是結構型設計模式中的第四個模式,也就是組合模式(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、在使用透明式的時候違背了接口分離原則,但是在使用安全式的時候又違背了依賴倒置原則
總結
到這里組合模式就介紹完了。這里需要提及的是在使用透明式組合模式時,樹葉構件繼承了操作子類的接口和行為,但是它並沒有子類。在接口分離原則中提到——客戶不應被強迫依賴它不使用的方法。所以這里違背了其原則,但是都遵循了依賴倒置原則,依賴於抽象。在實現安全式組合模式時,在客戶端調用時依賴於具體實現,也就違背了依賴倒置原則,但是卻將樹葉操作與樹枝構件操作分離,符合接口分離原則。在實現組合模式中不同形式實現有不同的問題。這就需要根據我們實際情況去衡量該如何使用了。
只有經歷過地獄般的折磨,才有征服天堂的力量。只有流過血的手指才能彈出世間的絕唱。
歡迎大家掃描下方二維碼,和我一起踏上設計模式的闖關之路吧!