我從上大學(xué)的時(shí)候開始編程,不過直到2000年左右才開始接觸設(shè)計(jì)模式,當(dāng)時(shí)已經(jīng)畢業(yè)并在一家軟件公司工作了一年多的時(shí)間,無論是編些代碼還是開發(fā)大型的商業(yè)軟件都算有了一些經(jīng)驗(yàn)了,但是記得當(dāng)時(shí)對(duì)設(shè)計(jì)模式的理解就是兩個(gè)字:不懂。我不明白的地方當(dāng)然很多,現(xiàn)在記得印象最深的就是“不懂”為什么要把很簡單的東西搞得那么復(fù)雜。后來隨著軟件開發(fā)經(jīng)驗(yàn)的增加才開始明白我所看到的“復(fù)雜”恰恰就是設(shè)計(jì)模式的精髓所在,我所理解的“簡單”就是一把鑰匙開一把鎖的模式,目的僅僅是著眼于解決現(xiàn)在的問題,而設(shè)計(jì)模式的“復(fù)雜”就在于它是要構(gòu)造一個(gè)“萬能鑰匙”,目的是提出一種對(duì)所有鎖的開鎖方案。在真正理解設(shè)計(jì)模式之前我一直在編寫“簡單”的代碼,這個(gè)“簡單”不是功能的簡單,而是設(shè)計(jì)的簡單。簡單的設(shè)計(jì)意味著缺少靈活性,代碼很鋼硬,只在這個(gè)項(xiàng)目里有用,拿到其它的項(xiàng)目中就是垃圾,我將其稱之為“一次性代碼”。在經(jīng)過多年的意氣風(fēng)發(fā),編寫了大量“實(shí)用”的代碼之后,我不得不一次次看到我的代碼被“使用以后拋棄”,這使我變得痛苦不堪。每當(dāng)我要開始編寫一個(gè)新程序或開發(fā)一個(gè)新項(xiàng)目時(shí),我會(huì)習(xí)慣性地把以前的代碼拿出來看看,找找有沒有可以直接在我的新項(xiàng)目中使用的東西,結(jié)果常常令我失望,除了一些功能性的代碼和編程技巧可以借鑒之外,其它的東西必須經(jīng)過修改才能使用。我就這樣“簡單”地修改,使其能夠在新項(xiàng)目中使用,然后再拋棄,再修改,再拋棄,直到一個(gè)同名的類有了多得我自己都記不清楚的修改版本的時(shí)候,我知道不能再繼續(xù)下去了,必須放棄編寫“簡單代碼”,于是我重新開始學(xué)習(xí)設(shè)計(jì)模式。
2001年的時(shí)候我開始為我的共享軟件“Winmsg消息精靈”開發(fā)新的4.0版,在吸取了以前的教訓(xùn)之后,我開始在新版本的開發(fā)中有意識(shí)地引入設(shè)計(jì)模式,之所以這樣做并不是為了嘩眾取寵,而是真的“痛定思痛”后的“改過自新”。使用設(shè)計(jì)模式為我?guī)砹藰O大的好處,我不再一味地重寫以前地代碼,而是盡量使用“適配器模式(Adapter Pattern)”和“外觀模式(FacadePattern)”重用它們,這樣減少了代碼編寫量,避免了大量的“體力勞動(dòng)”。在設(shè)計(jì)4.0版的插件系統(tǒng)時(shí),我引入了“抽象工廠模式(AbstractFactory Pattern)”、“橋模式(Bridge Pattern)”、訪問者模式(VisitorPattern)和“迭代器模式(IteatorPattern)”,這樣看起來使系統(tǒng)變得很復(fù)雜,但是在隨后的版本升級(jí)中體現(xiàn)了它們的價(jià)值。我的插件模式從最初的簡單數(shù)據(jù)處理功能到最后支持用戶界面,發(fā)生了巨大的改變,但是整個(gè)插件系統(tǒng)只經(jīng)過了很小的改動(dòng)就很好地適應(yīng)了它們,如果是使用以前的簡單設(shè)計(jì),恐怕又要重寫全部代碼了。使用設(shè)計(jì)模式不僅提高了代碼的可擴(kuò)展性,還提高了代碼的重用性,我在開發(fā)“消息精靈”的一個(gè)插件時(shí)設(shè)計(jì)了一套加密算法支持系統(tǒng),使用了“工廠模式(FactoryPattern)”、“單件模式(Singleton Pattern)”和“模板模式(TemplatePattern)”,這套系統(tǒng)后來應(yīng)用到了多個(gè)軟件項(xiàng)目中,直到最近開發(fā)一個(gè)基于HTTP協(xié)議的遠(yuǎn)程控制軟件時(shí),我依然沒有經(jīng)過任何修改就重用了這套系統(tǒng)。
很多我所認(rèn)識(shí)的程序員在接觸到設(shè)計(jì)模式之后,都有一種相見恨晚的感覺,有人形容學(xué)習(xí)了設(shè)計(jì)模式之后感覺自己好像已經(jīng)脫胎換骨,達(dá)到了新的境界,還有人甚至把是否了解設(shè)計(jì)模式作為程序員劃分水平的標(biāo)準(zhǔn)。但是設(shè)計(jì)模式確實(shí)不容易掌握,目前網(wǎng)上有很多關(guān)于設(shè)計(jì)模式的資料,也出版了很多關(guān)于設(shè)計(jì)模式的書,但都是針對(duì)模式的原理的介紹,或者說是對(duì)“GOFBook”的詳細(xì)講解手冊(cè),少量的代碼也僅僅是為了說明原理而設(shè)計(jì)的,對(duì)設(shè)計(jì)模式有心的初學(xué)者看到這些資料后很難對(duì)“如何使用設(shè)計(jì)模式指導(dǎo)實(shí)現(xiàn)代碼”產(chǎn)生感性上的認(rèn)識(shí),就更談不上深入地理性認(rèn)識(shí)設(shè)計(jì)模式了。其實(shí)學(xué)習(xí)設(shè)計(jì)模式并不在于死記硬背每種模式(即使是同一種模式,最終的實(shí)現(xiàn)結(jié)果也是五花八門),重點(diǎn)是如何在實(shí)際軟件開發(fā)中使用這些模式。使用的過程是循序漸進(jìn)的,開始是初級(jí)的使用(可能還包含理解的錯(cuò)誤,比如使用與上下文不匹配的模式),然后在使用的過程中加深對(duì)模式的理解,能夠靈活并正確地使用模式,并最終融會(huì)貫通,甚至創(chuàng)造出新的模式。
我本人在學(xué)習(xí)和嘗試使用設(shè)計(jì)模式的過程中,遇到了很多問題,也積累了一些經(jīng)驗(yàn),我在這里將其總結(jié)出來,拋磚引玉,希望對(duì)大家能夠有所幫助。以下是我在使用設(shè)計(jì)模式過程中的一些模式應(yīng)用實(shí)例,是這幾年經(jīng)驗(yàn)的一些總結(jié),體現(xiàn)了我對(duì)設(shè)計(jì)模式的一些理解,每個(gè)實(shí)例都有對(duì)應(yīng)的代碼例子,這些例子都是從我的軟件中摘錄出來的,為了重點(diǎn)體現(xiàn)模式的核心而做了一定的簡化。希望本文以及附帶的實(shí)例代碼,能夠幫助大家理解設(shè)計(jì)模式,為大家建立一座從抽象的模式到具體實(shí)現(xiàn)代碼之間的橋梁。由于認(rèn)識(shí)有限,錯(cuò)誤之處在所難免,歡迎拍磚。
以下是本文的撰寫計(jì)劃,我會(huì)在工作之余慢慢寫完它們,大家有什么意見可以隨時(shí)反饋給我:
inte2000@163.com 或者 simon@winmsg.com
[附錄:《設(shè)計(jì)模式應(yīng)用實(shí)例》撰寫計(jì)劃]
設(shè)計(jì)模式C++使用實(shí)例
第一章 引言
1.1 什么是設(shè)計(jì)模式
1.2 為什么要使用設(shè)計(jì)模式
1.3 怎樣使用設(shè)計(jì)模式
1.4 使用設(shè)計(jì)模式的幾個(gè)原則
第二章 “適配器模式(Adapter Pattern)”應(yīng)用實(shí)例
在我的個(gè)人計(jì)算機(jī)信息管理軟件“SysAdmin”中提供了用戶文件校驗(yàn)的功能,以前的代碼已經(jīng)對(duì)CRC32校驗(yàn)封裝了CCrc32Check類,現(xiàn)在需要支持另一種校驗(yàn)方法adler32,現(xiàn)在手上已經(jīng)有了一套adler32校驗(yàn)算法的代碼,只不過是以API的封裝形式提供的,怎樣將它們整合到原有的系統(tǒng)中呢?當(dāng)然是使用適配器模式了。
第三章 “外觀模式(Facade Pattern)”應(yīng)用實(shí)例
“TabbarsPlusIn for Visual C++6.0”是我發(fā)布的一款公開源碼的自由軟件,這個(gè)軟件的功能之一就是提供了整個(gè)VC工作區(qū)(Workspace)代碼打包壓縮和從壓縮文件中直接打開VC工作區(qū)查看代碼的功能,其中文件壓縮使用了Zlib庫,Zlib庫是一套API函數(shù)庫,不盡提供了數(shù)據(jù)壓縮的低級(jí)函數(shù),還提供了直接對(duì)文件壓縮的高級(jí)函數(shù),甚至還提供了對(duì)gzip文件的操作函數(shù),“Tabbars”只使用了這個(gè)庫的很少功能,因此創(chuàng)建了CZipFile和CUnzipFile兩個(gè)類專門操作這些函數(shù),這樣上層的功能實(shí)現(xiàn)模塊就不需要了解整個(gè)Zlib庫,只需使用這兩個(gè)類就行了,這其實(shí)就是一個(gè)外觀模式的應(yīng)用實(shí)例。
第四章 “單件模式(Singleton Pattern)”應(yīng)用實(shí)例
Singleton模式通常是與其它模式結(jié)合使用的,在我本人的共享軟件“Winmsg消息精靈”中使用的數(shù)據(jù)加密部分的設(shè)計(jì)應(yīng)用了“AbstractFactory”模式,AbstractFactory負(fù)責(zé)創(chuàng)建加密算子對(duì)象,但是需要一套機(jī)制來保證為使用者提供合適的ConcreteFactory,使用Singleton模式再合適不過了,這一章就介紹這樣一個(gè)Singleton模式,同時(shí)就在C++中使用Singleton模式模式進(jìn)行了簡單的探討(涉及友員函數(shù)和對(duì)象的創(chuàng)建刪除操作)。
第五章 “抽象工廠模式(Abstract Factory Pattern)”應(yīng)用實(shí)例
共享軟件“Winmsg消息精靈”中的很多類都需要使用加密算子對(duì)象對(duì)數(shù)據(jù)進(jìn)行加密,但是根據(jù)用戶界面的選擇或加密強(qiáng)度的要求,這些類可能需要根據(jù)實(shí)際情況決定使用哪一種加密算子,所以軟件的數(shù)據(jù)加密部分的設(shè)計(jì)應(yīng)用了“Abstract Factory”模式。
第六章 “生成器模式(Builder Pattern)”應(yīng)用實(shí)例
在基于HTTP協(xié)議的遠(yuǎn)程管理軟件HttpAdmin中,Controller和Agent之間要傳遞各種不同的消息,其中之一就是Controller向Provider發(fā)送的控制命令,Controller將控制命令統(tǒng)一發(fā)送給Agnet,Agent再將這些命令分派給已經(jīng)注冊(cè)到Agent的某個(gè)Provider,命令的種類很多,但是在Controller和Agent之間傳送的命令數(shù)據(jù)都由三部分組成,分別是命令的協(xié)議頭、命令參數(shù)和校驗(yàn)尾部,每種命令的三個(gè)部分組成各不相同,我們?cè)O(shè)計(jì)使用生成器模式,每個(gè)生成器負(fù)責(zé)生成一種命令的協(xié)議頭、參數(shù)段和校驗(yàn)尾部,并最終組合成一個(gè)完整的命令信息數(shù)據(jù)。
第七章 “訪問者模式(Visitor Pattern)”應(yīng)用實(shí)例
在基于HTTP協(xié)議的遠(yuǎn)程管理軟件HttpAdmin的設(shè)計(jì)中,Controller和Provider之間的數(shù)據(jù)傳輸有多種類型,比如文件上傳類型、文件下載類型、流數(shù)據(jù)上傳類型、流數(shù)據(jù)下載類型等等,它們分別對(duì)應(yīng)一個(gè)傳輸類型類,這些傳輸類型類都維護(hù)者一些類似的信息,比如上傳字節(jié)數(shù),下載字節(jié)數(shù),傳輸時(shí)間等等。在很多情況下需要統(tǒng)計(jì)這些信息,比如在界面上現(xiàn)實(shí)當(dāng)前的數(shù)據(jù)傳輸情況等等。統(tǒng)計(jì)這些信息可以有很多種方式,最簡單的就是通過這些類提供的屬性訪問函數(shù)一個(gè)一個(gè)計(jì)算,需要求和的就求和,需要計(jì)算平均值的就計(jì)算平均值。但是這樣一來代碼維護(hù)起來就比較困難了,每當(dāng)統(tǒng)計(jì)方式發(fā)生改變或添加新的統(tǒng)計(jì)信息時(shí),就需要查找所有的統(tǒng)計(jì)點(diǎn),修改統(tǒng)計(jì)點(diǎn)處的統(tǒng)計(jì)代碼,擴(kuò)展性和復(fù)用性差。有更好的設(shè)計(jì)方式嗎,當(dāng)然有,那就是訪問者模式,本章就結(jié)合這個(gè)例子介紹如何使用訪問者模式。
第八章 “橋模式(Bridge Pattern)”應(yīng)用實(shí)例
在基于HTTP協(xié)議的遠(yuǎn)程管理軟件HttpAdmin的設(shè)計(jì)中,Controller需要向某個(gè)Provider發(fā)送請(qǐng)求命令,并通過Provider的相應(yīng)完成一個(gè)命令的處理。這些命令有很多種,但是每種命令都要使用一種傳輸類型來發(fā)送和接收數(shù)據(jù),命令和傳輸類型都有相應(yīng)的封裝類(傳輸類型的封裝類在第七章已經(jīng)介紹了)。命令類型各種各樣,并且它們的處理方式也是大相徑庭,所以命令封裝類是一個(gè)變數(shù),同時(shí),傳輸類型也是一個(gè)變數(shù),因?yàn)閭鬏旑愋鸵灿泻芏喾N,那么如何在命令封裝類和傳輸類型封裝類這兩個(gè)變數(shù)之間建立聯(lián)系呢,使用橋模式就是最好的選擇。
第九章 “裝飾模式(Decorator Pattern)”應(yīng)用實(shí)例
使用License文件是軟件加密的一種方式,我們的個(gè)人計(jì)算機(jī)信息管理軟件“SysAdmin”使用CLicenseFile類處理對(duì)License文件的檢查,處理過程中需要對(duì)文件進(jìn)行CRC校驗(yàn),RSA數(shù)字簽名和統(tǒng)計(jì)MD5摘要,這個(gè)小系統(tǒng)使用了Decorator模式。
第十章 “組成模式(Compositor Pattern)”應(yīng)用實(shí)例
結(jié)合Windows的樹控件介紹怎樣將Compositor模式應(yīng)用到一個(gè)操作遞歸結(jié)構(gòu)數(shù)據(jù)類型的程序中。結(jié)合“基于HTTP協(xié)議的遠(yuǎn)程管理軟件HttpAdmin”中管理遠(yuǎn)程文件目錄信息的代碼,介紹了一個(gè)Compositor模式的應(yīng)用實(shí)例。
第十一章 “觀察者模式(Observer Pattern)”應(yīng)用實(shí)例
“基于HTTP協(xié)議的遠(yuǎn)程管理軟件”采用的是基于“Controller - Agent -Provider”的三層體系架構(gòu),其中“Controller - Agent”和“Provider -Agent”都是m:1的關(guān)系,多個(gè)Controller可以登陸到一個(gè)Agent上,同樣,多個(gè)Provider也可以注冊(cè)到一個(gè)Agent上,Agent負(fù)責(zé)處理所有的事件,比如新Provider注冊(cè)到Agent或Provider從Agent去注冊(cè)等等,這是一個(gè)典型的Observer模式,Controller向Agent訂閱(Subscribe)這些事件。
第十二章 “備忘錄模式(Memento Pattern)”應(yīng)用實(shí)例
在HttpAdmin中,Controller一次可能向Agent提交多個(gè)命令,如果因?yàn)榫W(wǎng)絡(luò)或其它原因?qū)е翽rovider不能完成這些命令,Controller就負(fù)責(zé)將這些命令信息(主要是命令參數(shù)和當(dāng)前命令的執(zhí)行狀態(tài))保存到磁盤文件上,等Controller下次啟動(dòng)時(shí)加載這個(gè)信息文件,使得所有的命令能夠恢復(fù)到上次的狀態(tài),由用戶選擇是否繼續(xù)執(zhí)行這些命令。這樣的設(shè)計(jì)涉及到對(duì)CControlCommand類的信息的訪問,但是CControlCommand類的一些參數(shù)信息和狀態(tài)信息是內(nèi)部私有的,如果提供對(duì)這些參數(shù)的訪問方法將破壞CControlCommand類的封裝性。既要能夠保存和恢復(fù)CControlCommand類的狀態(tài),又不能對(duì)CControlCommand類的設(shè)計(jì)做太大的改變,以至于要以破壞數(shù)據(jù)封裝為代價(jià),那么使用備忘錄模式就是最好的選擇了,本章將結(jié)合上面提到的例子介紹一個(gè)備忘錄模式的應(yīng)用實(shí)例。
第十三章 “模板模式(Template Pattern)”使用實(shí)例
個(gè)人計(jì)算機(jī)信息管理軟件“SysAdmin”采用支持插件的架構(gòu)體系,每種插件都有一個(gè)相應(yīng)的CxxxPlusIn與之對(duì)應(yīng),其基類是CPlusIn類,由于CPlusIn類的很多方法都是抽象方法,它們的實(shí)現(xiàn)需要延遲到相應(yīng)的CxxxPlusIn類實(shí)現(xiàn),所以CPlusIn類的設(shè)計(jì)采用了模板模式,本章將介紹模板模式的應(yīng)用實(shí)例。
第十四章 “命令模式(Command Pattern)”使用實(shí)例
在HttpAdmin的設(shè)計(jì)中,Controller向Agent提交命令是通過用戶的界面操作實(shí)現(xiàn)的,用戶使用界面上菜單、工具欄或命令行輸入接口輸入一個(gè)命令以及命令需要的參數(shù),然后Controller負(fù)責(zé)觸發(fā)相應(yīng)的命令處理類完成命令的處理過程。從接收命令參數(shù)到觸發(fā)相應(yīng)的命令類處理過程,我們?cè)O(shè)計(jì)使用了命令模式。使用命令模式,不盡省去了使用回調(diào)函數(shù)的煩惱,還使得系統(tǒng)具有了更好的可擴(kuò)展性。比如,我們的系統(tǒng)就可以利用Windows的多線程機(jī)制加快命令執(zhí)行的過程,將下載一個(gè)文件夾的命令(Command)委托給若干個(gè)下載單個(gè)文件的命令(Command)。
第十五章 “迭代器模式(Iteator Pattern)”使用實(shí)例
第十四章介紹的命令模式實(shí)例中,為了使每個(gè)命令(Command)不盡能夠觸發(fā)單個(gè)命令處理過程,還支持同時(shí)觸發(fā)多個(gè)命令處理過程(順序或并行),我們將每個(gè)Command設(shè)計(jì)成多個(gè)Children Commands的組合,CCommandList類被設(shè)計(jì)用來維護(hù)這些ChildrenCommands,這有點(diǎn)類的組合模式,但是組合模式更強(qiáng)調(diào)各個(gè)組合節(jié)點(diǎn)之間的關(guān)系,而Command關(guān)心的僅僅是有沒有ChildrenCommands的問題(通常至少有一個(gè))。在這個(gè)設(shè)計(jì)中,為了能夠方便地訪問CCommandList,迭代器模式便被引用進(jìn)來。本章就來介紹一個(gè)迭代器模式的實(shí)例。
聯(lián)系客服