【23種設計模式】組合模式(八)

2023-09-11 15:06:40

前言

組合模式,英文名稱是:Composite Pattern。當我們談到這個模式的時候,有一個物件和這個模式很像,也符合這個模式要表達的意思,那就是「俄羅斯套娃」。「俄羅斯套娃」就是大的瓷器娃娃裡面裝著一個小的瓷器娃娃,小的瓷器娃娃裡面再裝著更小的瓷器娃娃,直到最後一個不能再裝更小的瓷器娃娃的那個瓷器娃娃為止。在我們的作業系統中有資料夾的概念,資料夾可以包含資料夾,可以巢狀多層,最裡面包含的是檔案,這個概念和「俄羅斯套娃」很像。

組合模式的定義

客戶程式碼過多地依賴於物件容器複雜的內部實現結構,物件容器內部實現結構的變化將引起客戶程式碼的頻繁變化,帶來了程式碼的維護性、擴充套件性等方面的弊端。組合設計模式就是將物件組合成樹形結構以表示「部分-整體」的層次結構。Composite使得使用者對單個物件和組合物件的使用具有一致性。

組合模式的組成

  • 抽象構件角色(Component):這是個抽象角色,它給參加組合的物件定義出了公共的介面及預設行為,可以用來管理所有的子物件。在安全式的組合模式裡,構件角色並不定義出管理子物件的方法,這一定義由樹枝結構物件給出。

  • 樹葉構件角色(Leaf):樹葉物件是沒有下級子物件的物件,定義出參加組合的原始物件的行為。(原始物件的行為可以理解為沒有容器物件管理子物件的方法,或者 【原始物件行為】+【管理子物件的行為(Add,Remove等)】=面對客戶程式碼的介面行為集合)

  • 樹枝構件角色(Composite):代表參加組合的有下級子物件的物件,樹枝物件給出所有管理子物件的方法實現,如Add、Remove等。

組合模式的程式碼實現

組合模式有兩種實現方式,一種是:透明式的組合模式,另外一種是:安全式的組合模式。

所謂透明式是指「抽象構件角色」定義的介面行為集合包含兩個部分,一部分是葉子物件本身所包含的行為(比如Operation),另外一部分是容器物件本身所包含的管理子物件的行為(Add,Remove)。這個抽象構件必須同時包含這兩類物件所有的行為,使用者端程式碼才會透明的使用,無論呼叫容器物件還是葉子物件,介面方法都是一樣的,這就是透明

所謂安全式是指「抽象構件角色」只定義葉子物件的方法,確切的說這個抽象構件只定義兩類物件共有的行為,然後容器物件的方法定義在「樹枝構件角色」上,這樣葉子物件有葉子物件的方法,容器物件有容器物件的方法,這樣責任很明確,當然呼叫肯定不會丟擲異常了。

大家可以根據自己的情況自行選擇是實現為「透明式」還是「安全式」的,以下我們會針對這兩種情況都有實現,具體實現如下:

透明式

程式碼定義

 /// <summary>
    /// Transparent 透明式實現
    /// </summary>
    public class Transparent
    {
        /// <summary>
        /// 該抽象類就是資料夾抽象介面的定義,該型別就相當於是抽象構件Component型別
        /// </summary>
        public abstract class Folder
        {
            //增加資料夾或檔案
            public abstract void Add(Folder folder);

            //刪除資料夾或者檔案
            public abstract void Remove(Folder folder);

            //開啟檔案或者資料夾--該操作相當於Component型別的Operation方法
            public abstract void Open();
        }

        /// <summary>
        /// 該Word檔案類就是葉子構件的定義,該型別就相當於是Leaf型別,不能在包含子物件
        /// </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檔案不具有該功能");
            }

            //開啟檔案--該操作相當於Component型別的Operation方法
            public override void Open()
            {
                Console.WriteLine("開啟Word檔案,開始進行編輯");
            }
        }

        /// <summary>
        /// SonFolder型別就是樹枝構件,由於我們使用的是「透明式」,所以Add,Remove都是從Folder型別繼承下來的
        /// </summary>
        public class SonFolder : Folder
        {
            //增加資料夾或檔案
            public override void Add(Folder folder)
            {
                Console.WriteLine("檔案或者資料夾已經增加成功");
            }

            //刪除資料夾或者檔案
            public override void Remove(Folder folder)
            {
                Console.WriteLine("檔案或者資料夾已經刪除成功");
            }

            //開啟資料夾--該操作相當於Component型別的Operation方法
            public override void Open()
            {
                Console.WriteLine("已經開啟當前資料夾");
            }
        }
    }

呼叫實現

  public void RunTest()
        {
            //Folder myword = new Word();
            //myword.Open();//開啟檔案,處理檔案

            //myword.Add(new SonFolder());//丟擲異常
            //myword.Remove(new SonFolder());//丟擲異常


            Folder myfolder = new SonFolder();
            myfolder.Open();//開啟資料夾

            myfolder.Add(new SonFolder());//成功增加檔案或者資料夾
            myfolder.Remove(new SonFolder());//成功刪除檔案或者資料夾

            Console.Read();
        }

安全式

程式碼定義

  /// <summary>
    /// Secure 安全式實現
    /// </summary>
    public class Secure
    {
        /// <summary>
        /// 該抽象類就是資料夾抽象介面的定義,該型別就相當於是抽象構件Component型別
        /// </summary>
        public abstract class Folder //該型別少了容器物件管理子物件的方法的定義,換了地方,在樹枝構件也就是SonFolder型別
        {
            //開啟檔案或者資料夾--該操作相當於Component型別的Operation方法
            public abstract void Open();
        }

        /// <summary>
        /// 該Word檔案類就是葉子構件的定義,該型別就相當於是Leaf型別,不能在包含子物件
        /// </summary>
        public sealed class Word : Folder  //這型別現在很乾淨
        {
            //開啟檔案---該操作相當於Component型別的Operation方法
            public override void Open()
            {
                Console.WriteLine("開啟Word檔案,開始進行編輯");
            }
        }

        /// <summary>
        /// SonFolder型別就是樹枝構件,現在由於我們使用的是「安全式」,所以Add,Remove都是從此處開始定義的
        /// </summary>
        public abstract class SonFolder : Folder //這裡可以是抽象介面,可以自己根據自己的情況而定
        {
            //增加資料夾或檔案
            public abstract void Add(Folder folder);

            //刪除資料夾或者檔案
            public abstract void Remove(Folder folder);

            //開啟資料夾--該操作相當於Component型別的Operation方法
            public override void Open()
            {
                Console.WriteLine("已經開啟當前資料夾");
            }
        }

        /// <summary>
        /// NextFolder型別就是樹枝構件的實現類
        /// </summary>
        public sealed class NextFolder : SonFolder
        {
            //增加資料夾或檔案
            public override void Add(Folder folder)
            {
                Console.WriteLine("檔案或者資料夾已經增加成功");
            }

            //刪除資料夾或者檔案
            public override void Remove(Folder folder)
            {
                Console.WriteLine("檔案或者資料夾已經刪除成功");
            }

            //開啟資料夾--該操作相當於Component型別的Operation方法
            public override void Open()
            {
                Console.WriteLine("已經開啟當前資料夾");
            }
        }

    }

呼叫實現

  public void RunTest()
        {
            //這是安全的組合模式
            Folder myword = new Word();

            myword.Open();//開啟檔案,處理檔案


            Folder myfolder = new NextFolder();
            myfolder.Open();//開啟資料夾

            //此處要是用增加和刪除功能,需要轉型的操作,否則不能使用
            ((SonFolder)myfolder).Add(new NextFolder());//成功增加檔案或者資料夾
            ((SonFolder)myfolder).Remove(new NextFolder());//成功刪除檔案或者資料夾

        }

組合模式的優缺點

優點
  • 組合模式使得使用者端程式碼可以一致地處理物件和物件容器,無需關心處理的是單個物件,還是組合的物件容器。

  • 將」客戶程式碼與複雜的物件容器結構「解耦。

  • 可以更容易地往組合物件中加入新的構件。

缺點
  • 使得設計更加複雜。使用者端需要花更多時間理清類之間的層次關係。