話三國~建造者模式
急兄仇張飛遇害
話說劉備得知關羽遭到東吳殺害,怒不可遏立即準備大舉興兵伐吳,趙子龍諫曰:國賊乃曹操,並非孫權。曹丕竄漢,神人共怒!若陛下此時興兵,則關東義士並定一齊響應以迎王師;若捨曹丕而伐吳,兵勢一但交鋒,豈能隨便就結束。願陛下三思。劉備大怒曰:吾三人桃園結義,弒弟之仇不共戴天,卿何阻也?趙雲回道:兄弟之仇,私也;漢賊之仇,公也。豈可以私廢公。劉備已失理智,怒曰:朕不報弟仇,縱使擁有江山,又何足為貴?斥退趙雲,下令起兵。命張飛為車騎將軍,擔任伐吳先鋒,按下不表。
場景來到張飛營中,張飛正愁最近盔甲品質參差不齊,擔心伐吳不利,正在與工匠商量。場景先停在這裡,我們先來看看張飛目前遇到的問題。張飛營中目前正在趕工製造盔甲,盔甲的材料需要布料禦寒並且再增加鐵片來提升防禦力,至少要可以防禦遠方弓箭的攻擊。可是張飛營中最近造出來的盔甲不是忘了放布料,現在正值冬天,導致士兵紛紛遇寒而生病;不然就是有布料卻忘了放鐵片,造成堂堂重裝裝甲步兵,變成輕裝步兵,這情何以堪!張飛苦思良久,覺得一定是工匠在生產盔甲的程式碼出了問題,我們大家一起來幫張飛看看吧。
/// <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