(原創) 我的Design Pattern之旅[2]:Template Method Pattern (OO) (Design Pattern) (C++)

Abstract
template method pattern是我學到第二個pattern,算是一個很容易理解的pattern,但卻非常的實用。

Intent
對於operation,只先定義好演算法的輪廓,某些步驟則留給子類別去填補,以便在不改變演算法整體架構的情況下讓子類別去精鍊某些步驟。

其UML表示法


在實務上,我們可能本來有一個功能完整的class,但因為『需求改變』,新的class和原來的class幾乎60%相同, 只有40%不一樣,因此我們希望將60%相同部份的程式留下來,僅改寫40%不同的地方。

如本來某公司只生產『自動泡茶機』,後來為了增加產品線,想生產『自動泡咖啡機』,經過分析,兩台機器的架構相似,生產流程也相似。

自動泡茶機
step1 : 將開水煮開
step2 : 將<茶葉>放入開水
step3 : 將<茶>倒入杯子
step4 : 加上<檸檬>

自動泡咖啡機
step1 : 將開水煮開
step2 : 將<咖啡粉>放入開水
step3 : 將<咖啡>倒入杯子
step4 : 加上<糖>和<奶精>

很明顯的step1相同,但step2 ~ step4不相同,所以只需改寫step2 ~ step4,step1可以繼續使用。先設計一個DrinkMachine雛型,定義了生產過程和step1,因為step2 ~ step4各有差異,就留在繼承DrinkMachine的class去改寫,這就是template method pattern。


我們看看這個架構,日後若有新drink加入,DrinkMachine,TeaMachine,CoffeeMachine皆不用修改,符合OCP的closed for modification原則,若要加入新的class,只計程並改寫DrinkMachine即可,符合OCP的open for extension原則,所以是非常好維護的架構。

簡言之,template method pattern就是將不同的member function用class包起來,由derived class去改寫。

以下用C++實做template method pattern。

 1 ExpandedBlockStart.gif ContractedBlock.gif /**/ /* 
 2InBlock.gif(C) OOMusou 2007 http://oomusou.cnblogs.com
 3InBlock.gif
 4InBlock.gifFilename    : DP_TemplateMethodPattern1.cpp
 5InBlock.gifCompiler    : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
 6InBlock.gifDescription : Demo how to use Template Method Pattern
 7InBlock.gifRelease     : 03/31/2007 1.0
 8ExpandedBlockEnd.gif*/

 9 None.gif
10 None.gif#include  < iostream >
11 None.gif
12 None.gif using   namespace  std;
13 None.gif
14 ExpandedBlockStart.gifContractedBlock.gif class  DrinkMachine  dot.gif {
15InBlock.gifpublic:
16InBlock.gif  void makeDrink();
17InBlock.gif
18InBlock.gifprotected:
19ExpandedSubBlockStart.gifContractedSubBlock.gif  void boilWater() dot.gif{ cout << "boil some water" << endl; }
20InBlock.gif  virtual void doPutIngredient() const = 0;
21InBlock.gif  virtual void doPourInCup() const = 0;
22InBlock.gif  virtual void doAddFlavoring() const = 0;
23ExpandedBlockEnd.gif}
;
24 None.gif
25 ExpandedBlockStart.gifContractedBlock.gif void  DrinkMachine::makeDrink()  dot.gif {
26InBlock.gif  this->boilWater();
27InBlock.gif  this->doPutIngredient();
28InBlock.gif  this->doPourInCup();
29InBlock.gif  this->doAddFlavoring();
30ExpandedBlockEnd.gif}

31 None.gif
32 ExpandedBlockStart.gifContractedBlock.gif class  TeaMachine :  public  DrinkMachine  dot.gif {
33InBlock.gifprotected:
34ExpandedSubBlockStart.gifContractedSubBlock.gif  void doPutIngredient() const dot.gif{ cout << "steep tea in boiling water" << endl; }
35ExpandedSubBlockStart.gifContractedSubBlock.gif  void doPourInCup() const dot.gif{ cout << "pour tea in cup" << endl; } 
36ExpandedSubBlockStart.gifContractedSubBlock.gif  void doAddFlavoring() const dot.gif{cout << "add lemon" << endl; } 
37ExpandedBlockEnd.gif}
;
38 None.gif
39 ExpandedBlockStart.gifContractedBlock.gif class  CoffeeMachine :  public  DrinkMachine  dot.gif {
40InBlock.gifprotected:
41ExpandedSubBlockStart.gifContractedSubBlock.gif  void doPutIngredient() const dot.gif{ cout << "brew coffee in boiling water" << endl; }
42ExpandedSubBlockStart.gifContractedSubBlock.gif  void doPourInCup() const dot.gif{ cout << "pour coffee in cup" << endl; } 
43ExpandedSubBlockStart.gifContractedSubBlock.gif  void doAddFlavoring() const dot.gif{cout << "add sugar and milk." << endl; } 
44ExpandedBlockEnd.gif}
;
45 None.gif
46 ExpandedBlockStart.gifContractedBlock.gif int  main()  dot.gif {
47InBlock.gif  cout << "Making Teadot.gif" << endl;
48InBlock.gif    
49InBlock.gif  DrinkMachine *theDrinkMachine = &TeaMachine();
50InBlock.gif  theDrinkMachine->makeDrink();
51InBlock.gif  
52InBlock.gif  cout << endl;
53InBlock.gif  
54InBlock.gif  cout << "Making Coffeedot.gif" << endl;
55InBlock.gif  theDrinkMachine = &CoffeeMachine();
56InBlock.gif  theDrinkMachine->makeDrink();
57ExpandedBlockEnd.gif}


執行結果

None.gif Making Teadot.gif
None.gifboil some water
None.gifsteep tea in boiling water
None.gifpour tea in cup
None.gifadd lemon
None.gif
None.gifMaking Coffeedot.gif
None.gifboil some water
None.gifbrew coffee in boiling water
None.gifpour coffee in cup
None.gifadd sugar and milk.
None.gif


感謝Quark提供template版本的template method寫法

 1 None.gif #include  < iostream >
 2 None.gif
 3 None.gif using   namespace  std;
 4 None.gif
 5 None.giftemplate < typename T >  
 6 ExpandedBlockStart.gifContractedBlock.gif class  DrinkMachine  dot.gif
 7InBlock.gifpublic
 8ExpandedSubBlockStart.gifContractedSubBlock.gif  void makeDrink() dot.gif
 9InBlock.gif    T* derived =(T*this
10InBlock.gif    
11InBlock.gif    this->boilWater(); 
12InBlock.gif    derived->doPutIngredient(); 
13InBlock.gif    derived->doPourInCup(); 
14InBlock.gif    derived->doAddFlavoring(); 
15ExpandedSubBlockEnd.gif  }
  
16InBlock.gif  
17InBlock.gifprotected
18ExpandedSubBlockStart.gifContractedSubBlock.gif  void boilWater() dot.gif
19InBlock.gif      cout << "boil some water" << endl; 
20ExpandedSubBlockEnd.gif    }
 
21ExpandedBlockEnd.gif}

22 None.gif
23 ExpandedBlockStart.gifContractedBlock.gif class  TeaMachine :  public  DrinkMachine < TeaMachine >   dot.gif
24InBlock.giffriend DrinkMachine<TeaMachine>
25InBlock.gifprotected
26ExpandedSubBlockStart.gifContractedSubBlock.gif  void doPutIngredient() const dot.gif{ cout << "steep tea in boiling water" << endl; } 
27ExpandedSubBlockStart.gifContractedSubBlock.gif  void doPourInCup() const dot.gif{ cout << "pour tea in cup" << endl; } 
28ExpandedSubBlockStart.gifContractedSubBlock.gif  void doAddFlavoring() const dot.gif{cout << "add lemon" << endl; } 
29ExpandedBlockEnd.gif}

30 None.gif
31 ExpandedBlockStart.gifContractedBlock.gif class  CoffeeMachine: public  DrinkMachine < CoffeeMachine >   dot.gif
32InBlock.giffriend DrinkMachine<CoffeeMachine>
33InBlock.gifprotected
34ExpandedSubBlockStart.gifContractedSubBlock.gif  void doPutIngredient() const dot.gif{ cout << "brew coffee in boiling water" << endl; } 
35ExpandedSubBlockStart.gifContractedSubBlock.gif  void doPourInCup() const dot.gif{ cout << "pour coffee in cup" << endl; } 
36ExpandedSubBlockStart.gifContractedSubBlock.gif  void doAddFlavoring() const dot.gif{cout << "add sugar and milk." << endl; } 
37ExpandedBlockEnd.gif}

38 None.gif
39 ExpandedBlockStart.gifContractedBlock.gif int  main()  dot.gif
40InBlock.gif  cout << "Making Tea" << endl; 
41InBlock.gif  DrinkMachine<TeaMachine> *pTeaMachine = &TeaMachine(); 
42InBlock.gif  pTeaMachine->makeDrink(); 
43InBlock.gif  
44InBlock.gif  cout << endl; 
45InBlock.gif  
46InBlock.gif  cout << "Making Coffee" << endl; 
47InBlock.gif  DrinkMachine<CoffeeMachine> *pCoffeeMachine = &CoffeeMachine(); 
48InBlock.gif  pCoffeeMachine->makeDrink(); 
49ExpandedBlockEnd.gif}
 
50 None.gif


Remark
strategy和template method目的相同,皆對『新需求』的不同演算法提供『擴充』的機制,但手法卻不同,strategy採用object的方式,利用delegation改變algorithm,而template method則採用class的繼承方式來改變algorithm,由於用到的是class的inheritance,所以在compile-time就已經決定要override的algorithm,run-time就無法再改了,但strategy用的是object手法,所以在run-time還可以透過換object改變algorithm。

GoF的原文如下

None.gif Template methods use inheritance to vary part of an algorithm. Strategies use delegation to vary the entire algorithm.


See Also
(原創) 我的Design Pattern之旅[1]:Strategy Pattern (初級) (Design Pattern) (C++) (OO C++) (Template C++)

Reference
GoF,Design Patterns,Addison Weseley Longman,1995
Scott Meyers,Effective C++ 3/e Item 35,Addison Wesley,2005

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值