0 設(shè)計(jì)模式基礎(chǔ)
0.0 設(shè)計(jì)模式的定義
先來(lái)看一下設(shè)計(jì)模式常見的書面定義:
設(shè)計(jì)模式是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過(guò)分類編目的、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。使用設(shè)計(jì)模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。
設(shè)計(jì)模式是指在軟件開發(fā)中,經(jīng)過(guò)驗(yàn)證的,用于解決在特定環(huán)境下,重復(fù)出現(xiàn)的、特定問(wèn)題的解決方案。
設(shè)計(jì)模式更多的是一種實(shí)際應(yīng)用中經(jīng)驗(yàn)的基類和總結(jié),并得到了多數(shù)人的認(rèn)可和驗(yàn)證,經(jīng)過(guò)更規(guī)范的整理和分類及命名,成為了一種眾所周知的知識(shí)體系。
0.1 設(shè)計(jì)模式的分類
一般情況下說(shuō)到的設(shè)計(jì)模式都是指Gof著作中所講述的23中經(jīng)典設(shè)計(jì)模式。
創(chuàng)建型模式:?jiǎn)卫J?、抽象工廠模式、建造者模式、工廠模式、原型模式。
結(jié)構(gòu)型模式:適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式。
行為型模式:模版方法模式、命令模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、解釋器模式、狀態(tài)模式、策略模式、職責(zé)鏈模式(責(zé)任鏈模式)、訪問(wèn)者模式。
1 面向接口編程
上學(xué)的時(shí)候經(jīng)常跟室友一起玩兒War3,我一直比較鐘情于暗夜精靈族,暗夜精靈有4個(gè)灰常厲害的英雄,每個(gè)英雄都會(huì)有4個(gè)技能。剛開始學(xué)習(xí)面向?qū)ο笏枷氲臅r(shí)候,我已經(jīng)知道“英雄”可以定義成一個(gè)類,假設(shè)每個(gè)英雄出場(chǎng)時(shí)候都要秀一下自己華麗的四個(gè)技能,我們?cè)跇?gòu)造函數(shù)中傳遞英雄名稱,構(gòu)造不同的英雄對(duì)象。
/// <summary>/// 英雄類/// </summary>public class Hero { private readonly string _name; // 英雄名稱 /// <summary> /// 構(gòu)造函數(shù) /// </summary> /// <param name="name">英雄名稱</param> public Hero(string name) { _name = name; } /// <summary> /// 秀出自己的技能 /// </summary> public void ShowSkills() { switch (_name) { case "DH": Console.WriteLine("我是惡魔獵手,我會(huì)法力燃燒、獻(xiàn)祭、閃避和變身。"); break; case "WD": Console.WriteLine("我是守望者,我會(huì)暗影突襲、刀陣、閃爍和復(fù)仇天神。"); break; case "KOG": Console.WriteLine("我是叢林守護(hù)者,我會(huì)纏繞根須、荊棘光環(huán)、自然之力和寧?kù)o。"); break; case "POM": Console.WriteLine("我是月亮女祭司,我會(huì)召喚貓頭鷹,灼熱之箭,強(qiáng)擊光環(huán)和流星雨。"); break; default: break; } } }
英雄輪流秀出的自己的技能(排名不分先后)
class Program { static void Main(string[] args) { Hero dh = new Hero("DH"); dh.ShowSkills(); Hero wd = new Hero("WD"); wd.ShowSkills(); Hero kog = new Hero("KOG"); kog.ShowSkills(); Hero pom = new Hero("POM"); pom.ShowSkills(); } }
輸出結(jié)果
上面的代碼存在的問(wèn)題主要有2個(gè):
如果某天我不再玩兒暗夜精靈了,改玩兒別的種族,Hero類中的ShowSkills方法就要完全修改一遍。
秀出自己的技能只是一個(gè)最基本的行為,英雄還具有攻擊、移動(dòng)、釋放技能等行為,每個(gè)英雄攻擊力、移動(dòng)速度、釋放技能耗藍(lán)卻又都各不相同。
這里就涉及到了面向接口編程的重要原則。
通常接口用來(lái)定義實(shí)現(xiàn)類的外觀,提取實(shí)現(xiàn)類共有的行為定義。接口類似于一種契約,根據(jù)外部應(yīng)用的需要,約定了實(shí)現(xiàn)類應(yīng)該實(shí)現(xiàn)的功能,而具體內(nèi)部如何實(shí)現(xiàn),應(yīng)由具體的實(shí)現(xiàn)類控制,同時(shí)具體的實(shí)現(xiàn)類除了要實(shí)現(xiàn)接口規(guī)定的行為外,還可以根據(jù)需要實(shí)現(xiàn)自己獨(dú)有的行為,也就是說(shuō)實(shí)現(xiàn)類的功能應(yīng)包含但不僅限于接口定義的功能。
由于外部調(diào)用和內(nèi)部實(shí)現(xiàn)被接口隔離開了,外部調(diào)用只通過(guò)接口調(diào)用,也就是說(shuō)只要接口不變,內(nèi)部具體實(shí)現(xiàn)的變化就不會(huì)對(duì)外部調(diào)用產(chǎn)生任何影響。這樣使用接口的好處就很明顯了,當(dāng)我們?cè)黾悠渌⑿鄣臅r(shí)候只需要增加一個(gè)實(shí)現(xiàn)英雄接口的具體實(shí)現(xiàn)類既可,對(duì)原有已實(shí)現(xiàn)的不分不會(huì)造成任何影響。提現(xiàn)了接口“封裝隔離”的思想。
定義英雄接口:
/// <summary>/// 英雄接口定義/// </summary>public interface IHero { /// <summary> /// 秀技能 /// </summary> void ShowSkills(); }
不同英雄的具體實(shí)現(xiàn):
/// <summary>/// 惡魔獵手/// </summary>public class DH : IHero { /// <summary> /// 秀出自己的技能 /// </summary> public void ShowSkills() { Console.WriteLine("我是惡魔獵手,我會(huì)法力燃燒、獻(xiàn)祭、閃避和變身。"); } }
/// <summary>/// 守望者/// </summary>public class WD : IHero { /// <summary> /// 秀出自己的技能 /// </summary> public void ShowSkills() { Console.WriteLine("我是守望者,我會(huì)暗影突襲、刀陣、閃爍和復(fù)仇天神。"); } }
/// <summary>/// 叢林守護(hù)者/// </summary>public class KOG : IHero { /// <summary> /// 秀出自己的技能 /// </summary> public void ShowSkills() { Console.WriteLine("我是叢林守護(hù)者,我會(huì)纏繞根須、荊棘光環(huán)、自然之力和寧?kù)o。"); } }
/// <summary>/// 月亮女祭司/// </summary>public class POM : IHero { /// <summary> /// 秀出自己的技能 /// </summary> public void ShowSkills() { Console.WriteLine("我是月亮女祭司,我會(huì)召喚貓頭鷹,灼熱之箭,強(qiáng)擊光環(huán)和流星雨。"); } }
修改外部調(diào)用部分,同樣每個(gè)英雄都正確的秀出了自己的技能。
IHero dh = new DH(); dh.ShowSkills(); IHero wd = new WD(); wd.ShowSkills(); IHero kog = new KOG(); kog.ShowSkills(); IHero pom = new POM(); pom.ShowSkills(); Console.ReadLine();
到這里我們就會(huì)發(fā)現(xiàn)了新的問(wèn)題,上面說(shuō)過(guò)接口的作用的就是封裝隔離,外部調(diào)用只知道接口的存在,不應(yīng)該依賴于具體的實(shí)現(xiàn)類。而接口又是沒(méi)有辦法通過(guò)new關(guān)鍵字進(jìn)行實(shí)現(xiàn)話的,如何解決這個(gè)矛盾?
2 簡(jiǎn)單工廠模式
簡(jiǎn)單工廠模式并不包含在上述23種經(jīng)典設(shè)計(jì)模式之中,也有人說(shuō)簡(jiǎn)單工廠并不能算得上一個(gè)設(shè)計(jì)模式。不管怎么說(shuō),簡(jiǎn)單工廠卻是實(shí)實(shí)在在的簡(jiǎn)單易用,還記得當(dāng)年還在學(xué)校的時(shí)候,由于沒(méi)有經(jīng)過(guò)實(shí)際項(xiàng)目的歷練,在編碼經(jīng)驗(yàn)不足的情況下,去試著了解設(shè)計(jì)模式的時(shí)候,大部分設(shè)計(jì)模式是無(wú)法深入理解的,也就只有看到簡(jiǎn)單工廠模式的時(shí)候會(huì)驚呼:“原來(lái)還可以這樣”抑或“原來(lái)我一直用的就是簡(jiǎn)單工廠”。
簡(jiǎn)單來(lái)說(shuō),簡(jiǎn)單工廠就是通過(guò)定義一個(gè)工廠類,這個(gè)工廠類提供了一個(gè)創(chuàng)建具體實(shí)例的功能,外部調(diào)用只需要告訴工廠需要什么類型的實(shí)例,工廠負(fù)責(zé)創(chuàng)建這個(gè)實(shí)例,外部調(diào)用無(wú)需關(guān)心其具體實(shí)現(xiàn),從而達(dá)到真正的接口隔離的目的。
簡(jiǎn)單工廠類中創(chuàng)建具體實(shí)例的方法一般定義為靜態(tài)方法,從而可以避免在外部調(diào)用的時(shí)候再new簡(jiǎn)單工廠的對(duì)象,因此簡(jiǎn)單工廠模式一般也被成為靜態(tài)工廠模式。
我們定義一個(gè)簡(jiǎn)單工廠類。
/// <summary>/// 簡(jiǎn)單工廠類/// </summary>public class Factory { /// <summary> /// 創(chuàng)建英雄的靜態(tài)方法 /// </summary> /// <param name="heroName">英雄名稱</param> /// <returns></returns> public static IHero CreateHero(string heroName) { switch (heroName) { case "DH": return new DH(); case "WD": return new WD(); case "KOG": return new KOG(); case "POM": return new POM(); default: return null; } } }
然后外部調(diào)用的時(shí)候通過(guò)簡(jiǎn)單工廠方法創(chuàng)建各個(gè)英雄的實(shí)例。
IHero dh = Factory.CreateHero("DH"); dh.ShowSkills(); IHero wd = Factory.CreateHero("WD"); wd.ShowSkills(); IHero kog = Factory.CreateHero("KOG"); kog.ShowSkills(); IHero pom = Factory.CreateHero("POM"); pom.ShowSkills();
初識(shí)簡(jiǎn)單工廠模式的時(shí)候,最大的疑問(wèn)就是,這個(gè)簡(jiǎn)單工廠類,無(wú)非就是把原來(lái)在外部調(diào)用時(shí)創(chuàng)建具體英雄類實(shí)例的代碼挪了過(guò)去,在簡(jiǎn)單工廠類里面還是一樣需要通過(guò)new指定具體的類來(lái)進(jìn)行接口的實(shí)例化啊,而且還白白多了一個(gè)簡(jiǎn)單工廠類,意義何在?
其實(shí)簡(jiǎn)單工廠類最大的意義還是起到接口隔離的作用,看到隔離這個(gè)詞,就肯定有隔離的雙方(封裝體),我們多增加一個(gè)簡(jiǎn)單工廠類,表面上看是多了一個(gè)類,并沒(méi)有減少任何代碼,也沒(méi)有對(duì)代碼進(jìn)行大的更改,只是一個(gè)英雄實(shí)例化代碼的位置移動(dòng),簡(jiǎn)單工廠模式的精髓恰恰就是這個(gè)具體類實(shí)例化代碼位置的移動(dòng),我們知道,使用接口的目的就是不讓外部調(diào)用知道封裝體內(nèi)部的實(shí)現(xiàn),在使用簡(jiǎn)單工廠類之前,我們創(chuàng)建英雄實(shí)例的代碼很明顯的是位于外部調(diào)用部分的,這樣其實(shí)就是沒(méi)有隔離,由于簡(jiǎn)單工廠類位于封裝體內(nèi)部的,工廠類是可以知道具體的實(shí)現(xiàn)細(xì)節(jié)的。使用簡(jiǎn)單工廠類后,相當(dāng)于這個(gè)封裝提對(duì)外只公開了一個(gè)IHero接口及一個(gè)工廠類創(chuàng)建英雄的方法給外部調(diào)用,這樣隔離就很明確了,只是一段代碼位置的移動(dòng),從設(shè)計(jì)上來(lái)講,已經(jīng)發(fā)生了本質(zhì)的變化。
3 簡(jiǎn)單工廠的幾點(diǎn)建議
工廠方法靜化
簡(jiǎn)單工廠類中創(chuàng)建實(shí)例的方法,應(yīng)為靜態(tài)方法。
實(shí)例創(chuàng)建配置文件化
實(shí)例創(chuàng)建應(yīng)盡量通過(guò)配置文件及反射機(jī)制,動(dòng)態(tài)創(chuàng)建,達(dá)到能根據(jù)某個(gè)值,自動(dòng)判斷并創(chuàng)建對(duì)應(yīng)類的實(shí)例的目的,這樣就可以將龐大的swith語(yǔ)句塊消除,同時(shí),實(shí)例化部分的修改,只需要修改配置文件即可。
簡(jiǎn)單工廠模塊化
一個(gè)簡(jiǎn)單工廠可以定義多個(gè)創(chuàng)建實(shí)例的靜態(tài)方法,建議按照不同的功能模塊,創(chuàng)建不同的工廠類。因?yàn)楹?jiǎn)單工廠類是一個(gè)模塊封裝提的一部分。
4 簡(jiǎn)單工廠模式的優(yōu)點(diǎn)
封裝
能夠非常簡(jiǎn)單快捷的實(shí)現(xiàn)模塊的組件化,組件通過(guò)對(duì)外公開接口,實(shí)現(xiàn)面向接口編程
解耦
實(shí)現(xiàn)了外部調(diào)用和具體實(shí)現(xiàn)的解耦,增強(qiáng)了系統(tǒng)的健壯性和易維護(hù)性。