本文來自:崔成龍部落格專欄。轉載請註明出處:
http://blog.csdn.net/xiaoxian8023 軟考上午題終於考完了。三個趕考者都感覺不錯。檢查了2遍,提前30分鐘都出來了 。 小A,小B,小C樓下碰頭,相視一笑,輕鬆之感溢於言表。遂決定去吃面,以犒勞自己的肚子。
“老闆,我要西紅柿雞蛋面!”,“尖椒炸醬麵!”,“蘇格蘭打滷麵!”。。。。。。“好嘞!”
面快出鍋了,“哎哎,老闆,怎麼我的面跟他的面一樣啊,就是換了一下鹵?”往面裡放鹵的服務員翻了小B一眼,不搭理。旁邊那個年長點的師傅笑了笑,說道“小兄弟兒,這你就不懂了吧。其實面都是一樣的,只是換一下鹵罷了。”“哦哦,原來如此啊。”小B灰溜溜得端起面來跑開了。。。
面畢,小B實在是咽不下這口氣。就想在小C那兒“撈回點兒本來”。 “小C,面吃的怎麼樣啊?”小B陰陽怪氣的問道。 小C看著小B的表情,覺得有點不對勁,自己是不是又要上當了,但是又想不出來自己哪留有什麼“把柄”,然後就答道, “還,還行吧,問這幹嘛??” “嘿,你怕什麼呀。我考一個關於吃面的題目” “嗨,我還以為什麼呢,隨便考吧,哥是來者不拒。”小C心裡想到,就你那智商,能出什麼樣的難題呢,等著被鄙視吧。 “你感覺自家煮麵和飯館煮麵有什麼不同?” “這還用問!感覺不同唄。自己煮麵,自己累死了還得做飯,又是炒鹵,又是煮麵的,最低也得折騰半小時吧。飯館多好啊,只要吆喝一聲,人家都給你做好,端到你面前了。而且速度超快的。連這個都不知道。也對,就你那智商,好吧,我原諒你,哈哈哈哈” “哼,別笑得太早了。同樣是面?你說為什麼感覺不同啊” “呃,這個嘛,嗯,讓我想想”小C裝傻了。。。 “哈,有點意思哎。我給你說說看看對不。”小A憋不住了。接著說道, “剛才小C說了,自家煮麵,你必須自己做鹵,自己煮麵。每次想吃面的時候,這兩步都是少不了的。但是麵館不一樣啊。飯館鹵都是提前做好的。你點了面後,麵館給你煮麵,然後加上你要的鹵不就OK了嗎!而且速度超快。比自己做飯省事多了。” “哦~,原來如此。” “我說那會那個服務員像看白癡的一樣看我呀。”小B嘀咕道 “what,what!!原來你被吃癟了啊,哈哈”小C得意得捧腹大笑。 小A眼睛一轉,對即將要發飆的小B說道, “既然是你提出來的,你說說飯館為什麼要這麼做呢?” “呃,嗯,,,,我想,應該是為了更快捷,方便地方便使用者,同時提高自己的效率吧。” “還有沒有別的?” 小B想了半天,搖來搖頭,“想不到了” “提示一點,麵館在增加新類型面的時候,是怎麼做的?” “哦~~,我知道了,因為面差不多是一樣的,只需要準備鹵就行了。” “嗯,對的。其實面也是有不同的。比如說寬面,拉麵,圓面。這樣一組合,新的類型的面就出爐了。” “原來麵館裡也有這麼大學問啊。” “那是當然。365行,行行出狀元啊。先不說這個了,既然說道這個面了,那你用物件導向寫寫剛才我們討論的這件事兒吧。” “好。我試試吧”小B自通道。
既然是物件導向,那麼先把類找出來吧。首先有原料(Material),下分為面(
Noodles)和鹵(
Halogen)。面有寬麵條(WideNoodles),窄麵條(NarrowNoodles)等多種類型,滷菜有西紅柿雞蛋鹵(TomatoAndEgg),茄子豆瓣醬(AubergineBeanPaste)等多種滷菜。 西紅柿雞蛋面類(TomatoEggNoodles)為西紅柿雞蛋鹵和寬面構成,而蘇格蘭打滷麵(ScotlandNoodles)則是有茄子
豆瓣醬和窄面構成的。
先畫出UML圖:uml圖1.0版
“看看怎麼樣啊”,小B得瑟的說道。 “你有沒有考慮代碼的實現呢?”小A用看白癡的眼神掃了小B一眼,“除了C++,還有幾個物件導向的有多繼承啊!” “呃,這個,失誤失誤,我馬上改!” “別急啊,還有錯誤呢,西紅柿雞蛋面有兩部分構成,那肯定是用彙總或者是組合啊,不應該用繼承的”小A提醒道。 “哦,原來如此,我說怎麼感覺有點不對勁呢,等著吧”
5分鐘過後。。。 “哦,終於出來了,看看這次怎麼樣啊。”uml圖2.0.版
“西紅柿雞蛋面和蘇格蘭打滷麵的組成關係也畫出來了。嗯不錯,不過還有一點不對勁。假如我現在加一個新類型的面——西紅柿雞蛋滷麵(窄面),你怎麼加” “那就從麵條類裡泛化一個西紅柿雞蛋滷麵(窄面)類,然後再在它上面加上與西紅柿雞蛋鹵和窄面的組合關係” “那如果我現在麵館開10個分麵館,每個分麵館新增10樣類,你是不是這100種面都要重新添加一遍面和鹵的組合關係吧。” “那我還不得累死啊,這種活幹不得!”小B惶恐的說道。 “不管累,當你畫出UML圖的時候,能把你亂死。成品面與具體的面類型和滷菜類型的耦合性就太高了。想辦法鬆散這些耦合。” “那怎麼辦呢?”小B焦急的問道。 “呃,提醒你一點,你先對比一下你這兩版UML各自的特點。” “第一幅圖繼承關係比較明顯,第二幅圖則利用了組合關係,使其繼承關係簡單化了” “如果你把這兩個圖合成一個圖呢?” “這,這,怎麼合啊。” “看來還得我出馬啊,看著昂”UML圖3.0版
“不管什麼打滷麵,都抽象於滷麵類。滷麵類由麵條類和滷菜類組成。麵條類和滷菜類都有各自的子類。” “這個圖好面熟啊。讓我想想昂。哦,對了,這不是橋接模式嗎!” “是的,不過這樣做有什麼好處呢?” “這樣做有幾個好處,第一,鬆散耦合。具體的打滷麵不再與具體的面類型和鹵類型直接關聯,鬆散了它們之間的耦合;第二,在變化方面,具體的打滷麵, 具體的面類型和具體的鹵類型各自的變化,互不影響” “這樣我在開分麵館的時候就不用畫那麼多關係了,添加新類型的打滷麵不再成為難事。果然很厲害。哈哈哈哈”小B得瑟的笑著,好像他真的要開麵館似的。 “對頭!這就滿足了開閉原則,不用去修改,只需要添加即可。執行個體化具體的打滷麵時,在用戶端指定一下要哪種面,哪個鹵即可。”小A接著說, “其實這裡面最重要的是利用彙總組合關係,鬆散了耦合,使得抽象不再依賴於具體,而具體要依賴於抽象。” “哦,對哦。設計模式果然牛X。” “代碼留在晚上再寫吧,早點休息,下午還有考試呢!”小A看著一臉丫丫的小B,提醒道。 “哦哦,差點忘了還有考試了。希望下午碰到橋接模式,那我就。。。”小B繼續陷入丫丫ing。 小A搖搖頭,不再理睬小B,推開霸佔自己床鋪的小C,休息去了。
麵條類及子類
#region 麵條類及子類 /// <summary> /// 麵條類 /// </summary> public abstract class Noodles { public Noodles() { } /// <summary> /// 擷取面類型名稱 /// </summary> /// <returns></returns> public abstract string GetName(); } /// <summary> /// 窄麵條 /// </summary> public class NarrowNoodles:Noodles { public NarrowNoodles() { } public override string GetName() { return "窄麵條"; } } /// <summary> /// 寬麵條 /// </summary> public class WideNoodles : Noodles { public WideNoodles() { } public override string GetName() { return "寬麵條"; } } #endregion
滷菜類及子類
#region 滷菜類及子類 /// <summary> /// 滷菜類 /// </summary> public abstract class Halogen { public Halogen() { } /// <summary> /// 擷取滷菜名稱 /// </summary> /// <returns></returns> public abstract string GetName(); } /// <summary> /// 西紅柿雞蛋鹵 /// </summary> public class TomatoAndEgg : Halogen { public TomatoAndEgg() { } public override string GetName() { return "西紅柿雞蛋鹵"; } } /// <summary> /// 茄子豆瓣醬 /// </summary> public class AubergineBeanPaste:Halogen { public AubergineBeanPaste() { } public override string GetName() { return "茄子豆瓣醬"; } } #endregion
打滷麵類及子類
#region 打滷麵類及子類 /// <summary> /// 打滷麵類 /// </summary> public class NoodlesAndHalogen { protected string name; protected Noodles noodles; protected Halogen halogen; public NoodlesAndHalogen(string name, Noodles noodles, Halogen halogen) { this.name=name; this.noodles = noodles; this.halogen = halogen; } /// <summary> /// 擷取名稱及成分 /// </summary> public void GetName() { Console.WriteLine("我是"+name+",由"+noodles.GetName () +"和"+halogen.GetName ()+"組成"); } } /// <summary> /// 西紅柿雞蛋面 /// </summary> public class TomatoEggNoodles : NoodlesAndHalogen { public TomatoEggNoodles(string name, Noodles noodles, Halogen halogen) : base(name, noodles, halogen) { } } /// <summary> /// 蘇格蘭打滷麵 /// </summary> public class ScotlandNoodles : NoodlesAndHalogen { public ScotlandNoodles(string name, Noodles noodles, Halogen halogen) : base(name, noodles,halogen) { } }#endregion
用戶端代碼:
static void Main(string[] args) { NoodlesAndHalogen n1=new TomatoEggNoodles("西紅柿雞蛋面(寬面)",new WideNoodles(),new TomatoAndEgg() ); n1.GetName(); NoodlesAndHalogen n2 = new ScotlandNoodles("蘇格蘭打滷麵(窄面)", new NarrowNoodles(), new AubergineBeanPaste()); n2.GetName(); Console.Read(); }
運行結果: