C++設計模式之模板方法模式

臨近畢業,很多人都會到人才網發佈簡歷以尋求符合自己要求的崗位。登陸人才網,系統會給我們提供一份統一的模板,我們只需要按照要求填寫個人信息、教育背景、工作經歷、項目經驗等內容就可以了。雖然大家都是使用相同的模板,但每個人填寫的求職信息各不相同,簡歷也就各不一樣。在設計模式中,也存在類似的一種模式。實現某個功能需要多個步驟,其中有些步驟是固定的,有些步驟是不固定的,存在可變性,提供一個模板方法來定義這些步驟的執行次序,而通過其子類來覆蓋某些步驟,從而使得相同的算法框架可以有不同的執行結果,稱之爲模板方法模式。

1、模板方法模式

模板方法模式:定義一個操作中算法的框架,而將一些步驟延遲到子類中。模板方法模式使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟

    模板方法模式是一種基於繼承的代碼複用技術,它是一種類行爲型模式

    模板方法模式是結構最簡單的行爲型設計模式,在其結構中只存在父類與子類之間的繼承關係。通過使用模板方法模式,可以將一些複雜流程的實現步驟封裝在一系列基本方法中,在抽象父類中提供一個稱之爲模板方法的方法來定義這些基本方法的執行次序,而通過其子類來覆蓋某些步驟,從而使得相同的算法框架可以有不同的執行結果。模板方法模式提供了一個模板方法來定義算法框架,而某些具體步驟的實現可以在其子類中完成。

    模板方法模式結構比較簡單,其核心是抽象類和其中的模板方法的設計。

 模板方法模式結構圖

    (1) AbstractClass(抽象類)在抽象類中定義了一系列基本操作(PrimitiveOperations),這些基本操作可以是具體的,也可以是抽象的,每一個基本操作對應算法的一個步驟,在其子類中可以重定義或實現這些步驟。同時,在抽象類中實現了一個模板方法(Template Method),用於定義一個算法的框架,模板方法不僅可以調用在抽象類中實現的基本方法,也可以調用在抽象類的子類中實現的基本方法,還可以調用其他對象中的方法。

    (2) ConcreteClass(具體子類)它是抽象類的子類,用於實現在父類中聲明的抽象基本操作以完成子類特定算法的步驟,也可以覆蓋在父類中已經實現的具體基本操作。

    在實現模板方法模式時,開發抽象類的軟件設計師和開發具體子類的軟件設計師之間可以進行協作。一個設計師負責給出一個算法的輪廓和框架,另一些設計師則負責給出這個算法的各個邏輯步驟。實現這些具體邏輯步驟的方法即爲基本方法,而將這些基本方法彙總起來的方法即爲模板方法,模板方法模式的名字也因此而來。下面將詳細介紹模板方法和基本方法:

    1. 模板方法

    一個模板方法是定義在抽象類中的、把基本操作方法組合在一起形成一個總算法或一個總行爲的方法。這個模板方法定義在抽象類中,並由子類不加以修改地完全繼承下來。模板方法是一個具體方法,它給出了一個頂層邏輯框架,而邏輯的組成步驟在抽象類中可以是具體方法,也可以是抽象方法。

    2. 基本方法

    基本方法是實現算法各個步驟的方法,是模板方法的組成部分。基本方法又可以分爲2種:抽象方法、具體方法。

    (1) 抽象方法:一個抽象方法由抽象類聲明、由其具體子類實現。

    (2) 具體方法:一個具體方法由一個抽象類或具體類聲明並實現,其子類可以進行覆蓋也可以直接繼承。


2、SIM9000短信接收模塊的設計與實現

    我們平常使用的移動手機,很多都是使用SIM9000來實現通信功能。而對於短信接收模塊,接收到信息實際上是經過編碼的PDU串,得把接收到的PDU串進行譯碼後,纔是我們看到的真正短信內容。例如:當對方發送 "您好,北京歡迎您" ,我們接收到的是經過編碼後的PDU字符串"031648523486D596D5F8054901"。因此,得對PDU字符串進行譯碼,譯碼之後顯示"您好,北京歡迎您"。


SIM9000短信PDU協議格式

    本例中,爲了突出模板方法模式的思想,將簡化實現過程。對接收的短信,只關心發送人是誰、接收的數據長度、以及接收的數據內容。其它的信息不做處理。我們發現,對PDU字符串進行譯碼有三個基本操作,接收短信對短信內容進行譯碼顯示譯碼之後的短信內容。接收短信和顯示譯碼之後的內容是兩個固定的基本方法,而對短信進行譯碼是一個不固定的基本方法,可以使用(7轉8算法)對PDU串進行譯碼,也可以使用(8轉7算法)進行譯碼操作。

    請使用模板方法模式實現對PDU字符串進行7轉8譯碼,8轉7兩種譯碼。(具體兩種譯碼細節讀者不需要關心,只需要關注模板方法模式的思想就可以了)。

    對短信PDU串進行譯碼實現代碼如下:

  1. #ifndef _DECRYPT_H_  
  2. #define _DECRYPT_H_  
  3.   
  4. #include <iostream>  
  5. #include <string>  
  6. using namespace std;  
  7.   
  8.   
  9. //接收到的短消息  
  10. class ShortMsg  
  11. {  
  12. public:  
  13.     //基本方法: 接收編碼後的消息  
  14.     void RecvMsg()  
  15.     {  
  16.         cout << "接收編碼後的: <發送人> 字段內容" << endl;  
  17.         cout << "接收編碼後的: <消息長度> 字段內容" << endl;  
  18.         cout << "接收編碼後的: <數據內容> 字段內容" << endl;  
  19.         cout << "------------------------------------------" << endl << endl;  
  20.     }  
  21.   
  22.     //基本方法: 譯碼消息  
  23.     virtual void DecryptMsg() = 0;  
  24.   
  25.     //基本方法: 顯示譯碼後的消息  
  26.     void DisplayMsg()  
  27.     {  
  28.         cout << "顯示譯碼後的: <發送人> 字段內容" << endl;  
  29.         cout << "顯示譯碼後的: <消息長度> 字段內容" << endl;  
  30.         cout << "顯示譯碼後的: <數據內容> 字段內容" << endl;  
  31.         cout << "------------------------------------------" << endl << endl;  
  32.     }  
  33.   
  34.     //模板方法: 執行譯碼操作  
  35.     void ExcuteDecrypt()  
  36.     {  
  37.         RecvMsg();  
  38.         DecryptMsg();  
  39.         DisplayMsg();  
  40.     }  
  41. };  
  42.   
  43.   
  44. //7轉8譯碼  
  45. class SevenTo8Decrypt : public ShortMsg  
  46. {  
  47. public:  
  48.     //7轉8譯碼  
  49.     void DecryptMsg()  
  50.     {  
  51.         cout << "對編碼後的: <發送人> 字段內容進行7轉8譯碼" << endl;  
  52.         cout << "對編碼後的: <消息長度> 字段內容進行7轉8譯碼" << endl;  
  53.         cout << "對編碼後的: <數據內容> 字段內容進行7轉8譯碼" << endl;  
  54.         cout << "------------------------------------------" << endl << endl;  
  55.     }  
  56. };  
  57.   
  58.   
  59.   
  60. //8轉7譯碼  
  61. class EightTo7Decrypt : public ShortMsg  
  62. {  
  63. public:  
  64.     //8轉7譯碼  
  65.     void DecryptMsg()  
  66.     {  
  67.         cout << "對編碼後的: <發送人> 字段內容進行8轉7譯碼" << endl;  
  68.         cout << "對編碼後的: <消息長度> 字段內容進行8轉7譯碼" << endl;  
  69.         cout << "對編碼後的: <數據內容> 字段內容進行8轉7譯碼" << endl;  
  70.         cout << "------------------------------------------" << endl << endl;  
  71.     }  
  72. };  
  73.   
  74.   
  75. #endif  
    ShortMsg是抽象的短消息類,包含RecvMsg、DecryptMsg、DisplayMsg三個基本方法。ExcuteDecrypt是模板方法,用於把這三個基本方法按照一定的順序組合起來。SevenTo8Decrypt和EightTo7Decrypt是具體的譯碼算法類,實現抽象類中聲明的譯碼操作接口DecryptMsg。

    測試文件實現代碼如下:

  1. #include <iostream>  
  2. #include "Decrypt.h"  
  3. using namespace std;  
  4.   
  5. int main()  
  6. {  
  7.     //對接收到的消息進行7轉8譯碼  
  8.     ShortMsg * p7To8Decrypt = new SevenTo8Decrypt();  
  9.     p7To8Decrypt->ExcuteDecrypt();  
  10.           
  11.     delete p7To8Decrypt;  
  12.   
  13.     return 0;  
  14. }  
    編譯並執行,程序結果如下:


    如果需要使用其它的譯碼方法,只需要從抽象短消息類派生一個子類,實現父親中聲明的譯碼接口。無需修改之前的代碼。符合"開放封閉原則"。


3、模板方法模式總結

    模板方法模式是基於繼承的代碼複用技術,將公共的方法(一般都是不變的部分)封裝到父類中,而可變的部分可以通過繼承來繼續擴展,把變化部分和不變化部分分隔開。一方面能夠實現代碼複用,另一方面便於維護,它體現了面向對象的諸多重要思想,如封裝變化思想"找出應用中可能需要變化之處,把它們獨立出來,不要和那些不需要變化的代碼混在一起。換句話說,如果每次新的需求一來,都會使某些方面的代碼發生變化,那麼可以確定,這部分的代碼需要被抽象出來,和其他穩定的代碼有所區別"。模板方法把一些基本方法按照一定的順序組合起來,實現某個功能。如果不使用模板方法把這些基本方法按照一定的順序給組合起來,則子類可以使用任意的順序組合這些基本方法,導致程序邏輯混亂,無法控制。模式廣泛應用於框架設計中,以確保通過父類來控制處理流程的邏輯順序。

1.模板方法模式的主要優點

    (1) 在父類中形式化地定義一個算法,而由它的子類來實現細節的處理,在子類實現詳細的處理算法時並不會改變算法中步驟的執行次序。

    (2) 模板方法模式是一種代碼複用技術,它在類庫設計中尤爲重要,它提取了類庫中的公共行爲,將公共行爲放在父類中(不變的部分),而通過其子類來實現不同的行爲(異變部分),它鼓勵我們恰當使用繼承來實現代碼複用。

    (3) 可實現一種反向控制結構,通過子類覆蓋父類的鉤子方法來決定某一特定步驟是否需要執行。

    (4) 在模板方法模式中可以通過子類來覆蓋父類的基本方法,不同的子類可以提供基本方法的不同實現,更換和增加新的子類很方便,符合單一職責原則和開閉原則。

2.模板方法模式的主要缺點

    (1)需要爲每一個基本方法的不同實現提供一個子類,如果父類中可變的基本方法太多,將會導致類的個數增加,系統更加龐大,設計也更加抽象。

3.模板方法模式的具體應用

    (1)在遊戲角色中,存在魔鬼、天使、英雄等角色。這些角色都包含相同的建造過程(建造頭、腳、外觀等),而每一個遊戲角色建造方法各不相同。

    (2)需要將某些數據導出到TXT文本、數據庫、XML中。存在相同的過程,獲取數據,導出操作、查找已經導出的數據。

    (3)數據結構中的排序算法:定義一個抽象排序類,具體子類如快速排序、選擇排序、冒泡排序等實現基類中的排序算法。

    (4)對數據庫進行查詢操作:定義一個基類,包含連接數據庫、查詢數據、關閉連接等方法。不同的表,查詢操作是不一樣的。可以定義具體的查詢類,繼承於基類,實現基類中查詢接口,用於對不同的表進行查詢操作。

    (5)對於一些加密算法,包含許多按照某些順序執行的操作。而具體不同的加密算法,某個操作執行結果不同。

    (6)銀行部門採用的利息算法:活期賬戶和定期賬戶各使用不同的計算方法。

    (7)網上購物付款方式: 使用U頓則輸入密碼就可以付款。而使用短信驗證支付,會彈出一個對話框,要求我們輸入姓名、卡號、身份證號等信息。也就是說對於不同的支付方式,支付操作不同。

    (8)同樣是登陸某個系統,普通用戶登陸和管理員登陸,登陸後的界面存在某些差別。有些功能是普通用戶不具有的。

    (9)在一些UI控件中,可以從基類派生一個新的控件,重寫某些功能。例如:可以從Button類中繼承一個子類,在子類中覆蓋基類中的某些操作。
    (10)生活中模板: 求職簡歷;DNA雙螺旋結構;鳥叔江南Style風靡全球的時候,廣場舞大媽一步步分解動作,模仿鳥叔的舞姿;製作手機電腦等產品,需要按照某個過程來執行一系列操作;景德鎮陶瓷的製作等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章