1. 程式人生 > >通俗易懂設計模式解析——組合模式

通俗易懂設計模式解析——組合模式

前言

  今天介紹的是結構型設計模式中的第四個模式,也就是組合模式(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#設計模式系列目錄

歡迎大家掃描下方二維碼,和我一起踏上設計模式的闖關之路吧!