C++设计模式:Template Method
我使用過一個(gè)簡單的后臺(tái)服務(wù)框架.這個(gè)框架上手很容易,我只需要繼承一個(gè)基類,同時(shí)實(shí)現(xiàn),或重寫(override)基類聲明的幾個(gè)接口(這些接口聲明為虛函數(shù),或者純虛函數(shù)),然后調(diào)用基類定義好的run()函數(shù),便可以將框架代碼運(yùn)行起來.run函數(shù)做的事情,是依序調(diào)用上述的幾個(gè)接口:
class Service { public : int run(){// ....step1(); // 收包 , 解包step2(); // 業(yè)務(wù)邏輯處理step3(); // 回包step4(); //資源釋放//.... } protected: virtual int step1(){ // 收包,解包邏輯實(shí)現(xiàn) //...} virtual int step3(){ // 回包邏輯實(shí)現(xiàn) //... } virtual int step4(){ //資源釋放 //... } virtual int step2() =0 ; //純虛函數(shù),派生類實(shí)現(xiàn) }其中收包,解包,回包,釋放資源等動(dòng)作,框架會(huì)提供一份實(shí)現(xiàn),由于我們有時(shí)候會(huì)采用其他的數(shù)據(jù)協(xié)議,所以基類也將收包回包等函數(shù)聲明為虛函數(shù),允許我們針對(duì)新的協(xié)議進(jìn)行函數(shù)的重寫(override).而對(duì)于業(yè)務(wù)邏輯處理函數(shù),也就是step2,框架無法為我們實(shí)現(xiàn),我們需要根據(jù)具體的業(yè)務(wù)需求來實(shí)現(xiàn)該函數(shù),在派生類中來實(shí)現(xiàn)step2函數(shù):
class MyService : public Service{ int step2(){ // 具體業(yè)務(wù)邏輯的實(shí)現(xiàn) } }派生類實(shí)現(xiàn)了step2函數(shù)后,通過調(diào)用run函數(shù)來運(yùn)行程序:
void main (){ //...準(zhǔn)備工作 Service * myService = new MyService(); if( myService->run()){//...后續(xù)處理 } // ... }我們的后臺(tái)服務(wù)框架例子中,run函數(shù)定義了一個(gè)服務(wù)的穩(wěn)定執(zhí)行步驟,但某個(gè)步驟有著特定的需求無法馬上定義,需要延遲到派生類中去實(shí)現(xiàn),這時(shí)候就需要采用模板方法模式.模板方法模式要解決的問題是:如何在確定穩(wěn)定操作結(jié)構(gòu)的前提下,靈活地應(yīng)對(duì)各個(gè)子步驟的變化或者晚期實(shí)現(xiàn)需求?
李建忠老師曾提過,重構(gòu)獲得設(shè)計(jì)模式(Refactoring to Patterns).設(shè)計(jì)模式的應(yīng)用不宜先入為主,一上來就使用設(shè)計(jì)模式是對(duì)設(shè)計(jì)模式的最大誤用,在敏捷軟件開發(fā)中,提倡使用的是通過重構(gòu)來獲得設(shè)計(jì)模式,這也是最好的使用設(shè)計(jì)模式的方法.
而關(guān)于重構(gòu)的關(guān)鍵技法,包括了:
- 靜態(tài)->動(dòng)態(tài)
- 早綁定->晚綁定
- 繼承->組合
- 編譯時(shí)依賴->運(yùn)行時(shí)依賴
- 緊耦合->松耦合
接下來我們來看看如何將一個(gè)程序,重構(gòu)成模板方法模式.現(xiàn)代軟件專業(yè)分工之后,也出現(xiàn)了"框架與應(yīng)用程序的劃分",框架實(shí)現(xiàn)人員先定義好框架的執(zhí)行流程,也就是算法骨架(穩(wěn)定),并提供可重寫(overide)的接口(變化)給應(yīng)用開發(fā)人員,以開發(fā)適應(yīng)其應(yīng)用程序的子步驟.模板方法通過晚綁定,實(shí)現(xiàn)了框架與應(yīng)用程序之間的松耦合.
現(xiàn)在我們需要實(shí)現(xiàn)一個(gè)程序庫,需要四個(gè)步驟來完成相應(yīng)功能.其中step1,step3步驟穩(wěn)定,而step2,step4則根據(jù)不同應(yīng)用的具體需要,自行定義其具體功能,庫開發(fā)人員無法預(yù)先實(shí)現(xiàn)好step2,step4.那么庫開發(fā)人員可以先寫好:
// 庫開發(fā)人員 class Library{public:void step1(){// 步驟1的具體實(shí)現(xiàn)//...}void step3(){//步驟3的具體實(shí)現(xiàn)//...}應(yīng)用程序開發(fā)人員則根據(jù)具體的應(yīng)用需求,來實(shí)現(xiàn)剩余的兩個(gè)步驟:
//應(yīng)用程序開發(fā)人員 class Application{ public:void step2(){//步驟2的具體實(shí)現(xiàn)//...}bool step4(){//步驟4的具體實(shí)現(xiàn)//...} }然后應(yīng)用程序開發(fā)人員還需要寫一個(gè)main方法,將步驟以某種流程串起來:
//穩(wěn)定public static void main(String args[]) {Library lib = new Library();Application app = new Application();lib.step1();if (app.step2()) {lib.step3();}app.step4();}這種辦法實(shí)際上是一種C語言結(jié)構(gòu)化的實(shí)現(xiàn)方式,雖然用的是C++,但沒有體現(xiàn)出面向?qū)ο蟮奶匦詠?main方法中,四個(gè)步驟的調(diào)用過程是相對(duì)穩(wěn)定的,我們可以把這種穩(wěn)定提升到庫的實(shí)現(xiàn)中去,而應(yīng)用程序開發(fā)人員,只需要實(shí)現(xiàn)"變化"的代碼即可.這就引出了第二種做法.
第二種做法,是庫開發(fā)人員不僅實(shí)現(xiàn)step1(),step3(),同時(shí)將step2(),step4()聲明為純虛函數(shù),等待應(yīng)用程序開發(fā)人員自己去實(shí)現(xiàn)這兩個(gè)純虛函數(shù).注意到,main方法中定義的執(zhí)行流程是相對(duì)穩(wěn)定的,完全可以把這些步驟移動(dòng)到庫類中去.
//庫開發(fā)人員 class Library{public :void run (){step1(); if(step2()){ //支持變化-->虛函數(shù)的多態(tài)調(diào)用step3();}step4(); //支持變化-->虛函數(shù)的多態(tài)調(diào)用} protected:void step1(){ //穩(wěn)定//... }void step3(){ //穩(wěn)定//...}virtual bool step2() =0; //純虛函數(shù)virtual void step4() = 0; //純虛函數(shù) virtual ~Library(){//...} };注意step2,step4為純虛函數(shù),這是因?yàn)閹扉_發(fā)人員無法知道怎么寫,留給程序庫開發(fā)人員來實(shí)現(xiàn),也就是"把實(shí)現(xiàn)延遲",這在C++中體現(xiàn)為虛函數(shù)或純虛函數(shù),由應(yīng)用程序開發(fā)人員繼承Library以實(shí)現(xiàn)兩個(gè)純虛函數(shù).這一段代碼實(shí)際上體現(xiàn)了大部分設(shè)計(jì)模式應(yīng)用的特點(diǎn),也就是在穩(wěn)定中包含著變化,run函數(shù)的算法流程是穩(wěn)定的,但是算法的某個(gè)步驟是可變的,可變的延遲實(shí)現(xiàn):
class Application: public Library{ protected:virtual bool step2(){//...子類重寫實(shí)現(xiàn)}virtual void step4(){//...子類重寫實(shí)現(xiàn)} };然后,應(yīng)用程序開發(fā)人員,只需要通過多態(tài)指針來完成框架的使用:
public static void main(String args[]) {Library * ptr = new Application();ptr->run();delete ptr; }指針ptr是一個(gè)多態(tài)指針,它聲明為Library類型,實(shí)際指向的對(duì)象為Application類型對(duì)象.它會(huì)調(diào)用到基類的run方法,遇到step2,step4函數(shù)時(shí),通過虛函數(shù)機(jī)制,調(diào)用到派生類實(shí)現(xiàn)的step2,step4函數(shù).
回顧兩種實(shí)現(xiàn)方式,我們可以發(fā)現(xiàn),第一種實(shí)現(xiàn)方式中:
- 庫開發(fā)者負(fù)責(zé)step1,step3 ;
- 應(yīng)用程序開發(fā)者負(fù)責(zé)step2,step4,執(zhí)行流程(穩(wěn)定)
采用了模板方法模式的實(shí)現(xiàn)方式中:
- 庫開發(fā)者負(fù)責(zé)step1,step3,執(zhí)行流程(穩(wěn)定)
- 應(yīng)用程序開發(fā)者負(fù)責(zé)step2,step4
一般來說,框架/組件/庫的實(shí)現(xiàn),總是要先于應(yīng)用程序的開發(fā)的.在第一種方式中,應(yīng)用程序開發(fā)者(晚開發(fā))的執(zhí)行流程調(diào)用了庫開發(fā)者定義好的函數(shù)(早開發(fā)),稱為早綁定,而反過來在模板方法模式中,庫開發(fā)者在執(zhí)行流程中先調(diào)用了step2,step4函數(shù),而這兩個(gè)函數(shù)需要延遲到應(yīng)用程序開發(fā)人員真正實(shí)現(xiàn)時(shí),才通過虛函數(shù)機(jī)制進(jìn)行調(diào)用,這種方式則稱為早綁定.這便是重構(gòu)使用設(shè)計(jì)模式的技法: 早綁定->晚綁定.
回過頭來看看模板方法模式的定義:定義一個(gè)操作中的算法的骨架,而將一些步驟延遲到子類中.Template Method使得子類刻意不改變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟.(<> GoF) 所謂的骨架,要求是相對(duì)穩(wěn)定的,在上面的例子中,如果step1,step3也是不穩(wěn)定的,那么該情景下就不適用于適用設(shè)計(jì)模式,原因是軟件體系中所有的東西都不穩(wěn)定.設(shè)計(jì)模式的假設(shè)條件是必須有一個(gè)穩(wěn)定點(diǎn),如果沒有穩(wěn)定點(diǎn),那么設(shè)計(jì)模式?jīng)]有任何作用.反過來說,如果所有的步驟都是穩(wěn)定的,這種極端情況也不適用于適用設(shè)計(jì)模式.設(shè)計(jì)模式總是處理"穩(wěn)定中的變化"這種情景.設(shè)計(jì)模式最大的作用,是在穩(wěn)定與變化之間尋找隔離點(diǎn),然后來分離它們,從而來管理變化.從而我們也能夠得到啟發(fā),學(xué)會(huì)分析出軟件體系結(jié)構(gòu)中哪部分是穩(wěn)定的,哪部分是變化的,是學(xué)好設(shè)計(jì)模式的關(guān)鍵點(diǎn).
再來看一看模板方法設(shè)計(jì)模式的結(jié)構(gòu):其中TemplateMethod() 方法也就是我們上面所說的run函數(shù),它相對(duì)穩(wěn)定,primitiveOperation1(),primitiveOperation2()為兩個(gè)變化的函數(shù),可由派生類實(shí)現(xiàn),在TemplateMethod()中調(diào)用步驟.在下圖中,紅色圈為穩(wěn)定的部分,而黑色圈為變化的部分.
在面向?qū)ο蟮臅r(shí)代,絕大多數(shù)的框架設(shè)計(jì)都使用了模板方法模式.作為一個(gè)應(yīng)用程序開發(fā)人員,我們往往只需要實(shí)現(xiàn)幾個(gè)步驟,框架便會(huì)把我們的步驟"串接"到執(zhí)行流程中,有時(shí)候甚至連main函數(shù)都不用我們?nèi)?shí)現(xiàn).這樣子也有弊端,我們看不見框架的執(zhí)行流程,執(zhí)行細(xì)節(jié)是怎么樣的,往往有一種"只見樹木不見森林"的感覺.
最后來總結(jié)以下模板方法設(shè)計(jì)模式.Template Method設(shè)計(jì)模式是一種非常基礎(chǔ)性的設(shè)計(jì)模式,它要解決的問題是如何在確定穩(wěn)定操作結(jié)構(gòu)的前提下,來靈活應(yīng)對(duì)各個(gè)子步驟的變化或者晚期實(shí)現(xiàn)需求.它使用了函數(shù)的多態(tài)性機(jī)制,為應(yīng)用程序框架提供了靈活的拓展點(diǎn),是代碼復(fù)用方面的基本實(shí)現(xiàn)結(jié)構(gòu).Template Method設(shè)計(jì)模式明顯劃分了穩(wěn)定與變化的關(guān)系,除了靈活應(yīng)對(duì)子步驟的變化外,也是晚綁定的典型應(yīng)用,通過反向控制結(jié)構(gòu),使得早期的代碼可以調(diào)用晚期代碼.而在具體實(shí)現(xiàn)上,被Template Method調(diào)用的虛函數(shù),可以具有實(shí)現(xiàn),也可以沒有任何實(shí)現(xiàn),這在C++中體現(xiàn)為虛函數(shù)或者純虛函數(shù),一般將這些函數(shù)設(shè)置為proteced方法.
總結(jié)
以上是生活随笔為你收集整理的C++设计模式:Template Method的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mysql 批量写入数据 性能优化
- 下一篇: JS中apply和call的联系和区别