**本文來自:崔成龍博客專欄。轉載請注明出處:**[**http://blog.csdn.net/xiaoxian8023**](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();
}
~~~
運行結果:

[](http://my.csdn.net/my/favorite/miniadd?t=%25E9%259D%25A2%25E4%25B8%258E%25E5%258D%25A4%25E7%259A%2584%25E9%25B9%258A%25E6%25A1%25A5%25E7%259B%25B8%25E4%25BC%259A%25E2%2580%2594%25E2%2580%2594%25E6%25A1%25A5%25E6%258E%25A5%25E6%25A8%25A1%25E5%25BC%258F&u=http://blog.csdn.net/xiaoxian8023/article/details/7642352)