[設計模式-2] 建造者模式

話三國~建造者模式

急兄仇張飛遇害

話說劉備得知關羽遭到東吳殺害,怒不可遏立即準備大舉興兵伐吳,趙子龍諫曰:國賊乃曹操,並非孫權。曹丕竄漢,神人共怒!若陛下此時興兵,則關東義士並定一齊響應以迎王師;若捨曹丕而伐吳,兵勢一但交鋒,豈能隨便就結束。願陛下三思。劉備大怒曰:吾三人桃園結義,弒弟之仇不共戴天,卿何阻也?趙雲回道:兄弟之仇,私也;漢賊之仇,公也。豈可以私廢公。劉備已失理智,怒曰:朕不報弟仇,縱使擁有江山,又何足為貴?斥退趙雲,下令起兵。命張飛為車騎將軍,擔任伐吳先鋒,按下不表。

場景來到張飛營中,張飛正愁最近盔甲品質參差不齊,擔心伐吳不利,正在與工匠商量。場景先停在這裡,我們先來看看張飛目前遇到的問題。張飛營中目前正在趕工製造盔甲,盔甲的材料需要布料禦寒並且再增加鐵片來提升防禦力,至少要可以防禦遠方弓箭的攻擊。可是張飛營中最近造出來的盔甲不是忘了放布料,現在正值冬天,導致士兵紛紛遇寒而生病;不然就是有布料卻忘了放鐵片,造成堂堂重裝裝甲步兵,變成輕裝步兵,這情何以堪!張飛苦思良久,覺得一定是工匠在生產盔甲的程式碼出了問題,我們大家一起來幫張飛看看吧。

  /// <summary>
    /// 盔甲建造者
    /// </summary>
    abstract class  IArmorBuilder
    {
       protected bool hasPartA = false;
       protected bool hasPartB = false;

       public abstract void BuildPartA();
       public abstract void BuildPartB();
       public abstract void GetArmor();
    }

首先,張飛發現盔甲的建造是由一個叫做IArmorBuilder的抽象類別在控制的,這個抽象類別有一個叫做GreenArmorBuilder的實作,負責實踐盔甲的作法。(這裡不要問我為什麼是綠色,因為不知道從何時開始,蜀國綠色,魏國藍色,吳國紅色的概念早已深植人心。看來藍綠惡鬥,最早是起源於三國時代XD)。

    /// <summary>
    /// 正統綠色盔甲
    /// </summary>
    class GreenArmorBuilder : IArmorBuilder
    {
        public override void BuildPartA()
        {
            hasPartA = true;
            Console.WriteLine("加入綠色布料");
        }

        public override void BuildPartB()
        {
            hasPartB = true;
            Console.WriteLine("加入綠色盔甲鐵片");
        }

        /// <summary>
        /// 其實正確是要回傳Armor 用void只是不想再多定義Armor 
        /// </summary>
        public override void GetArmor()
        {
            if (!hasPartA)
            {
              Console.WriteLine("盔甲沒布料 士兵冷死了");
            }
            else if(!hasPartB)
            {
              Console.WriteLine("盔甲沒防禦力 坑爹阿");
            }
            else 
            {
               Console.WriteLine("士兵得到一件綠色重裝盔甲");
            }
        }
    }

我們實際看一下GreenArmorBuilder的類別,他實做了BuilderPartA和BuilderParB,分別負責加入布料和加入鐵片,看來並沒有甚麼問題。而GArmor方法也有做了防呆,在發現缺乏布料的時候,會提示士兵會冷;沒有加盔甲,會提示士兵缺乏防禦力,其實,這也不算防呆,因為產品已經產出了,它只能描述一個事實,卻不能為失敗的盔甲在做任何修正。可看到這裡,還是看不出有甚麼問題阿,因為GreenArmorBuilder類別確實有實作加入布料和加入鐵片的方法呀!這時候,張飛想到了雖然盔甲規定是這樣建造,但真正在建造盔甲的是工匠呀,如果工匠出了問題,那裝備當然會有問題!於是張飛前往鍛造場,查看一下工匠端的程式碼。

class Program
    {
        static void Main(string[] args)
        {
            var GreenBuilderA = new GreenArmorBuilder();
            GreenBuilderA.BuildPartA();
            GreenBuilderA.GetArmor();
            Console.WriteLine();

            var GreenBuilderB = new GreenArmorBuilder();
            GreenBuilderB.BuildPartB();
            GreenBuilderB.GetArmor();
            Console.WriteLine();

            var GreenBuilderC = new GreenArmorBuilder();
            GreenBuilderC.BuildPartA();
            GreenBuilderC.BuildPartB();
            GreenBuilderC.GetArmor();

            Console.Read();
        }
    }

程式碼輸出:
加入綠色布料
盔甲沒防禦力 坑爹阿

加入綠色盔甲鐵片
盔甲沒布料 士兵冷死了

加入綠色布料
加入綠色盔甲鐵片
士兵得到一件綠色重裝盔甲

 

各位看官有發現問題嗎?張飛發現問題了(就說光榮三國志的設定,張飛的智力設定有偏見,都不超過40,張飛如果穿越到現代的話也許會當程式設計師),張飛看到裝甲在製作的時候工匠有時候忘記呼叫加入布料的方法,有時候忘記呼叫加入鐵片的方法,造成裝備生產出來,就會有參差不齊的品質!但我這裡要強調,工匠並沒有貪污喔,單純是事情太多了,真的會不小心忘記。張飛心裡盤算著,這個問題要先解決,不然沒辦法幫二哥報仇!要冷靜,要細心,這個問題一定有解的!終於,張飛發現了一些端睨,"建造盔甲的過程是穩定的,都一定要加布料和加鐵片,否則就不是好盔甲" ,也許具體建造的細節可能會不同,那是後話,這裡先留個伏筆。但對張飛來講,我不管這些有的沒的,我只要你給我的盔甲是可禦寒有防禦力的就可以,我跟你要一個你就給我一個這種盔甲,我跟你要1000個你就給我1000個,其他的你自己搞定。這時候張飛想起了一個設計模式,

建造者模式=>可把複雜的"建造邏輯"和"組合的過程"分離

這會是一個很好的解決方案。而此模式的精隨在於,你只要跟我說你要甚麼盔甲,具體是怎麼做的,怎麼組裝的你不用去關心。

  /// <summary>
    /// 指揮者類別
    /// </summary>
    class Director
    {
        public void Construct(IArmorBuilder builder)
        {
            builder.BuildPartA();
            builder.BuildPartB();
        }
    }

張飛花了五分鐘,便完成了一個叫做Director的類別,負責指揮工匠每件盔甲的製作,畢竟厲害的工匠年紀都大了,記性不太好很容易忘東忘西。我們從這裡也可以看出張飛對報二哥的仇是有多麼心急,一下子就能寫出程式碼!那我們就來看看張飛是怎麼讓程式碼運作的,張飛的這個類別,它只有提供一個方法,便是Construct(組合),由指揮者類別來負責告訴工匠要加入布料,要加入鐵片,這樣工匠就不會忘記了,我們來實際看看張飛接下來的生產流程會有多順利。

 class Program
    {
        static void Main(string[] args)
        {
            var director = new Director();

            for(int i = 1; i <= 3; i++)
            {
                var Armor = new GreenArmorBuilder();
                director.Construct(Armor);
                Armor.GetArmor();
                Console.WriteLine();
            }
         
            Console.Read();
        }
    }

程式碼輸出:
加入綠色布料
加入綠色盔甲鐵片
士兵得到一件綠色重裝盔甲

加入綠色布料
加入綠色盔甲鐵片
士兵得到一件綠色重裝盔甲

加入綠色布料
加入綠色盔甲鐵片
士兵得到一件綠色重裝盔甲

張飛笑開了,裝備的品質得到了很大的一個改善。但張飛並沒有高興很久,想到二哥的仇,張飛的心如刀割,隨即一個念頭,我要把盔甲全部變成白色的,為二哥掛孝!隨即傳令,令部將范疆、張達要在一個月內準備好三軍的白色盔甲!范疆、張達一聽,隨即喊到怎可能!!這起碼要三個月以上時間準備,張飛聽罷大怒,你當俺沒寫過程式阿!這樣子的需求要它奶奶的三個月,你大爺我恨不得今天就殺到東吳老家,立即命令拖下去打一百大板!!!並烙下狠話,一個月內不準備好,我立刻殺了你們!!!

范疆、張達回到營中,心想,反正做不到也是死,倒不如殺了張飛投靠東吳,說不定能換個高官來當。否則,一個月後必死無疑,討論過後,當晚就刺殺了張飛,可惜張飛大仇未報,死的不明不白,令人不勝噓唏。難道張飛錯了嗎?這樣的需求真的一個月內無法完成,我們來看看到底要改些甚麼吧。

  /// <summary>
    /// 服喪用白色盔甲
    /// </summary>
    class WhiteArmorBuilder : IArmorBuilder
    {
        public override void BuildPartA()
        {
            hasPartA = true;
            Console.WriteLine("加入白色布料");
        }

        public override void BuildPartB()
        {
            hasPartB = true;
            Console.WriteLine("加入白色盔甲鐵片");
        }

        /// <summary>
        /// 其實正確是要回傳Armor 用void只是不想再多定義Armor 
        /// </summary>
        public override void GetArmor()
        {
            if (!hasPartA)
            {
                Console.WriteLine("盔甲沒布料 士兵冷死了");
            }
            else if (!hasPartB)
            {
                Console.WriteLine("盔甲沒防禦力 坑爹阿");
            }
            else
            {
                Console.WriteLine("士兵得到一件白色重裝盔甲");
            }
        }
    }

首先,當然是要加入一個WhiteArmorBuilder 的類別,並且實做了IArmorBuilder,把BuildPartA的實作內容改成"加入白色布料";把BuildPartB的實作內容改成"加入白色盔甲鐵片",接著還要改甚麼?

    class Program
    {
        static void Main(string[] args)
        {
            var director = new Director();

            for(int i = 1; i <= 3; i++)
            {
                //var Armor = new GreenArmorBuilder();<===改一下類別名稱而已
                var Armor = new WhiteArmorBuilder();
                director.Construct(Armor);
                Armor.GetArmor();
                Console.WriteLine();
            }
         
            Console.Read();
        }
    }

甚麼,只需要改實做的子類別變成新擴充的whiteArmorBuilder就大功告成了,這也完全符合開閉原則,我們要變更需求真的非常彈性,只要擴充一個類別即可。所以,言歸正傳,張飛錯了嗎?張飛沒錯,這樣的需求,根本不需要三個月(即使加上真正的製造過程),張飛錯的是,跟一群沒有程式魂的部屬談論設計模式。張飛的遺言應該是,原來懂設計模式也是一種錯阿!!吾死不瞑目 ....End