[设计模式-2] 建造者模式

话三国~建造者模式


急兄仇张飞遇害

话说刘备得知关羽遭到东吴杀害,怒不可遏立即准备大举兴兵伐吴,赵子龙谏曰:国贼乃曹操,并非孙权。曹丕窜汉,神人共怒!若陛下此时兴兵,则关东义士并定一齐响应以迎王师;若舍曹丕而伐吴,兵势一但交锋,岂能随便就结束。愿陛下三思。刘备大怒曰:吾三人桃园结义,弑弟之仇不共戴天,卿何阻也?赵云回道:兄弟之仇,私也;汉贼之仇,公也。岂可以私废公。刘备已失理智,怒曰:朕不报弟仇,纵使拥有江山,又何足为贵?斥退赵云,下令起兵。命张飞为车骑将军,担任伐吴先锋,按下不表。

场景来到张飞营中,张飞正愁最近盔甲品质参差不齐,担心伐吴不利,正在与工匠商量。场景先停在这里,我们先来看看张飞目前遇到的问题。张飞营中目前正在赶工制造盔甲,盔甲的材料需要布料御寒并且再增加铁片来提升防御力,至少要可以防御远方弓箭的攻击。可是张飞营中最近造出来的盔甲不是忘了放布料,现在正值冬天,导致士兵纷纷遇寒而生病;不然就是有布料却忘了放铁片,造成堂堂重装装甲步兵,变成轻装步兵,这情何以堪!张飞苦思良久,觉得一定是工匠在生产盔甲的程序出了问题,我们大家一起来帮张飞看看吧。

  /// 
    /// 盔甲建造者
    /// 
    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)。

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

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

        /// 
        /// 其实正确是要回传Armor 用void只是不想再多定义Armor 
        /// 
        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个,其他的你自己搞定。这时候张飞想起了一个设计模式,

建造者模式=>可把复杂的"建造逻辑"和"组合的过程"分离

这会是一个很好的解决方案。而此模式的精随在于,你只要跟我说你要什么盔甲,具体是怎么做的,怎么组装的你不用去关心。

  /// 
    /// 指挥者类
    /// 
    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();
        }
    }

程序输出:
加入绿色布料
加入绿色盔甲铁片
士兵得到一件绿色重装盔甲

加入绿色布料
加入绿色盔甲铁片
士兵得到一件绿色重装盔甲

加入绿色布料
加入绿色盔甲铁片
士兵得到一件绿色重装盔甲

张飞笑开了,装备的品质得到了很大的一个改善。但张飞并没有高兴很久,想到二哥的仇,张飞的心如刀割,随即一个念头,我要把盔甲全部变成白色的,为二哥挂孝!随即传令,令部将范疆、张达要在一个月内准备好三军的白色盔甲!范疆、张达一听,随即喊到怎可能!!这起码要三个月以上时间准备,张飞听罢大怒,你当俺没写过程序阿!这样子的需求要它奶奶的三个月,你大爷我恨不得今天就杀到东吴老家,立即命令拖下去打一百大板!!!并烙下狠话,一个月内不准备好,我立刻杀了你们!!!

范疆、张达回到营中,心想,反正做不到也是死,倒不如杀了张飞投靠东吴,说不定能换个高官来当。否则,一个月后必死无疑,讨论过后,当晚就刺杀了张飞,可惜张飞大仇未报,死的不明不白,令人不胜嘘唏。难道张飞错了吗?这样的需求真的一个月内无法完成,我们来看看到底要改些什么吧。

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

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

        /// 
        /// 其实正确是要回传Armor 用void只是不想再多定义Armor 
        /// 
        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