C++设计模式之模板模式(template)(行为型)
一 定義
模板方法模式是一種類(lèi)的行為型模式,在它的結(jié)構(gòu)圖中只有類(lèi)之間的繼承關(guān)系,沒(méi)有對(duì)象關(guān)聯(lián)關(guān)系,模板方法模式(Template Method Pattern)官方定義:定義一個(gè)操作中的算法的框架,而將一些步驟延遲到子類(lèi)中,使得子類(lèi)可以不改變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟。
抽象模板中包含三種類(lèi)型的方法:?基本方法、模板方法和鉤子方法(Hook Method)。
基本方法——基本方法也叫做基本操作,是由子類(lèi)實(shí)現(xiàn)的方法,并且在模板方法被調(diào)用。
模板方法——核心方法,不允許子類(lèi)重寫(xiě),所以都會(huì)加上final修飾符,可以有一個(gè)或幾個(gè),一般是一個(gè)具體方法框架,按照固定的流程對(duì)基本方法的調(diào)度
鉤子方法——為了讓模板方法的執(zhí)行結(jié)果的更好地適應(yīng)因外界條件改變。比如說(shuō)銀行辦理業(yè)務(wù)為例,辦理業(yè)務(wù)是個(gè)模板方法,普通人要經(jīng)歷排隊(duì)、取號(hào)、等待、辦理四個(gè)基本流程,而Vip則不需要排隊(duì)、取號(hào)、等待,那么在設(shè)計(jì)的時(shí)候我們的抽象模板類(lèi)需要考慮到,于是乎你得需要在模板方法各基本方法調(diào)用之前增加條件判斷,那么用于設(shè)置這個(gè)條件的方法就是鉤子方法(Hook Method),鉤子方法也可以是抽象的還可以由子類(lèi)的一個(gè)方法返回值決定公共部分的執(zhí)行結(jié)果。
二 ULM圖
角色:
(1)AbstractClass:是抽象類(lèi),其實(shí)也就是一個(gè)抽象模板,定義并實(shí)現(xiàn)了一個(gè)模板方法。這個(gè)模板方法一般是一個(gè)具體方法,它給出了一個(gè)頂級(jí)邏輯的框架,而邏輯的組成步驟在相應(yīng)的抽象操作中,推遲到子類(lèi)實(shí)現(xiàn)。頂級(jí)邏輯也有可能調(diào)用一些具體方法。
(2)ConcreteClass:實(shí)現(xiàn)父類(lèi)所定義的一個(gè)或多個(gè)抽象方法。每一個(gè)AbstractClass都可以有任意多個(gè)ConcreteClass與之對(duì)應(yīng),而每一個(gè)ConcreteClass都可以給出這些抽象方法(也就是頂級(jí)邏輯的組成步驟)的不同實(shí)現(xiàn),從而使得頂級(jí)邏輯的實(shí)現(xiàn)各不相同。
總結(jié):當(dāng)不變的和可變的行為在方法的子類(lèi)實(shí)現(xiàn)中混合在一起的時(shí)候,不變的行為就會(huì)在子類(lèi)中重復(fù)出現(xiàn)。我們通過(guò)模板方法模式,把這些行為搬移到單一的地方,這樣幫助子類(lèi)擺脫重復(fù)的不變行為的糾纏。
模板方法模式的優(yōu)點(diǎn)
良好的擴(kuò)展性,封裝不變部分,擴(kuò)展可變部分,把認(rèn)為是不變部分的算法封裝到父類(lèi)實(shí)現(xiàn),而可變部分的則可以通過(guò)繼承來(lái)繼續(xù)擴(kuò)展。例如增加一個(gè)新的功能很簡(jiǎn)單,只要再增加一個(gè)子類(lèi),實(shí)現(xiàn)父類(lèi)的基本方法就可以了。
提取公共部分代碼,便于維護(hù),減小維護(hù)升級(jí)成本,基本操作由父類(lèi)定義,子類(lèi)實(shí)現(xiàn)
基本方法是由子類(lèi)實(shí)現(xiàn)的,因此子類(lèi)可以通過(guò)擴(kuò)展的方式增加相應(yīng)的功能,符合開(kāi)閉原 則。
模板方法模式的缺點(diǎn)
通常抽象類(lèi)是負(fù)責(zé)聲明某一類(lèi)的事物的共同屬性和抽象方法,實(shí)現(xiàn)類(lèi)則是完成定義具體的特性和方法。但是模板方法模式卻顛倒了,抽象類(lèi)定義了部分抽象方法,由子類(lèi)實(shí)現(xiàn),子類(lèi)執(zhí)行的結(jié)果影響了父類(lèi)的結(jié)果,也就是子類(lèi)對(duì)父類(lèi)產(chǎn)生了影響。每個(gè)不同的實(shí)現(xiàn)都需要定義一個(gè)子類(lèi),這會(huì)導(dǎo)致類(lèi)的個(gè)數(shù)增加,系統(tǒng)更加龐大,設(shè)計(jì)也更加抽象,但是更加符合“單一職責(zé)原則”,使得類(lèi)的內(nèi)聚性得以提高。
適合使用模板方法模式的場(chǎng)景
?四 實(shí)例
1. 填寫(xiě)簡(jiǎn)歷表
最近有個(gè)招聘會(huì),可以帶上簡(jiǎn)歷去應(yīng)聘了。但是,其中有一家公司不接受簡(jiǎn)歷,而是給應(yīng)聘者發(fā)了一張簡(jiǎn)歷表,上面有基本信息、教育背景、工作經(jīng)歷等欄,讓?xiě)?yīng)聘者按照要求填寫(xiě)完整。每個(gè)人拿到這份表格后,就開(kāi)始填寫(xiě)。如果用程序?qū)崿F(xiàn)這個(gè)過(guò)程,該如何做呢?一種方案就是用模板方法模式:定義一個(gè)操作中的算法的骨架,而將一些步驟延遲到子類(lèi)中。模板方法使得子類(lèi)可以不改變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟。我們的例子中,操作就是填寫(xiě)簡(jiǎn)歷這一過(guò)程,我們可以在父類(lèi)中定義操作的算法骨架,而具體的實(shí)現(xiàn)由子類(lèi)完成。下面給出它的UML圖。
?
#include <iostream> #include <memory>//簡(jiǎn)歷 class Resume { protected: //保護(hù)成員virtual void SetPersonalInfo() {}virtual void SetEducation() {}virtual void SetWorkExp() {} public:void FillResume(){SetPersonalInfo();SetEducation();SetWorkExp();} };class ResumeA: public Resume { protected:void SetPersonalInfo(){std::cout << "A's PersonalInfo" << std::endl;}void SetEducation(){std::cout << "A's Education" << std::endl;}void SetWorkExp(){std::cout << "A's Work Experience" << std::endl;} };class ResumeB: public Resume { protected:void SetPersonalInfo(){std::cout << "B's PersonalInfo" << std::endl;}void SetEducation(){std::cout << "B's Education" << std::endl;}void SetWorkExp(){std::cout << "B's Work Experience" << std::endl;} };int main() {auto r1 = std::make_unique<ResumeA>();r1->FillResume();auto r2 = std::make_unique<ResumeB>();r2->FillResume();return 0; }?
五?模式的擴(kuò)展
1)模板方法模式與控制反轉(zhuǎn)(好萊塢原則)
在模板方法模式中,子類(lèi)不顯式調(diào)用父類(lèi)的方法,而是通過(guò)覆蓋父類(lèi)的方法來(lái)實(shí)現(xiàn)某些具體的業(yè)務(wù)邏輯,父類(lèi)控制對(duì)子類(lèi)的調(diào)用,這種機(jī)制被稱為好萊塢原則(Hollywood Principle),好萊塢原則的定義為:“不要給我們打電話,我們會(huì)給你打電話(Don‘t call us, we’ll call you)”。在好萊塢,把簡(jiǎn)歷遞交給演藝公司后就只有回家等待。由演藝公司對(duì)整個(gè)娛樂(lè)項(xiàng)的完全控制,演員只能被動(dòng)式的接受公司的差使,在需要的環(huán)節(jié)中,完成自己的演出。模板方法模式充分的體現(xiàn)了“好萊塢”原則。由父類(lèi)完全控制著子類(lèi)的邏輯,子類(lèi)不需要調(diào)用父類(lèi),而通過(guò)父類(lèi)來(lái)調(diào)用子類(lèi),子類(lèi)可以實(shí)現(xiàn)父類(lèi)的可變部份,卻繼承父類(lèi)的邏輯,不能改變業(yè)務(wù)邏輯。
2)模板方法模式符合開(kāi)閉原則
模板方法模式意圖是由抽象父類(lèi)控制頂級(jí)邏輯,并把基本操作的實(shí)現(xiàn)推遲到子類(lèi)去實(shí)現(xiàn),這是通過(guò)繼承的手段來(lái)達(dá)到對(duì)象的復(fù)用,同時(shí)也遵守了開(kāi)閉原則。
父類(lèi)通過(guò)頂級(jí)邏輯,它通過(guò)定義并提供一個(gè)具體方法來(lái)實(shí)現(xiàn),我們也稱之為模板方法。通常這個(gè)模板方法才是外部對(duì)象最關(guān)心的方法。在上面的銀行業(yè)務(wù)處理例子中,templateMethodProcess這個(gè)方法才是外部對(duì)象最關(guān)心的方法。所以它必須是public的,才能被外部對(duì)象所調(diào)用。
子類(lèi)需要繼承父類(lèi)去擴(kuò)展父類(lèi)的基本方法,但是它也可以覆寫(xiě)父類(lèi)的方法。如果子類(lèi)去覆寫(xiě)了父類(lèi)的模板方法,從而改變了父類(lèi)控制的頂級(jí)邏輯,這違反了“開(kāi)閉原則”。我們?cè)谑褂媚0宸椒J綍r(shí),應(yīng)該總是保證子類(lèi)有正確的邏輯。所以模板方法應(yīng)該定義為final的。所以AbstractClass類(lèi)的模板方法templateMethodProcess方法應(yīng)該定義為final。
模板方法模式中,抽象類(lèi)的模板方法應(yīng)該聲明為final的。因?yàn)樽宇?lèi)不能覆寫(xiě)一個(gè)被定義為final的方法。從而保證了子類(lèi)的邏輯永遠(yuǎn)由父類(lèi)所控制。
3)模板方法模式與對(duì)象的封裝性
面向?qū)ο蟮娜筇匦?#xff1a;繼承,封裝,多態(tài)。
對(duì)象有內(nèi)部狀態(tài)和外部的行為。封裝是為了信息隱藏,通過(guò)封裝來(lái)維護(hù)對(duì)象內(nèi)部數(shù)據(jù)的完整性。使得外部對(duì)象不能夠直接訪問(wèn)一個(gè)對(duì)象的內(nèi)部狀態(tài),而必須通過(guò)恰當(dāng)?shù)姆椒ú拍茉L問(wèn)。
對(duì)象屬性和方法賦予指定的修改符(public、protected、private)來(lái)達(dá)到封裝的目的,使得數(shù)據(jù)不被外部對(duì)象惡意的訪問(wèn)及方法不被錯(cuò)誤調(diào)用導(dǎo)造成破壞對(duì)象的封裝性。
降低方法的訪問(wèn)級(jí)別,也就是最大化的降低方法的可見(jiàn)度是一種很重要的封裝手段。最大化降低方法的可見(jiàn)度除了可以達(dá)到信息隱藏外,還能有效的降低類(lèi)之間的耦合度,降低一個(gè)類(lèi)的復(fù)雜度。還可以減少開(kāi)發(fā)人員發(fā)生的的錯(cuò)誤調(diào)用。
一個(gè)類(lèi)應(yīng)該只公開(kāi)外部需要調(diào)用的方法。而所有為public方法服務(wù)的方法都應(yīng)該聲明為protected或private。如是一個(gè)方法不是需要對(duì)外公開(kāi)的,但是它需要被子類(lèi)進(jìn)行擴(kuò)展的或調(diào)用。那么把它定義為protected.否則應(yīng)該為private。
顯而易見(jiàn),模板方法模式中的聲明為abstract的基本操作都是需要迫使子類(lèi)去實(shí)現(xiàn)的,它們僅僅是為模板方法服務(wù)的。它們不應(yīng)該被抽象類(lèi)(AbstractClass)所公開(kāi),所以它們應(yīng)該protected。
因此模板方法模式中,迫使子類(lèi)實(shí)現(xiàn)的抽象方法應(yīng)該聲明為protected abstract。
4)模板方法與勾子方法(hookMethod)
模板方法模式的抽象類(lèi)定義方法:
模板方法:一個(gè)模板方法是定義在抽象類(lèi)中的、把基本操作方法組合在一起形成一個(gè)總算法或一個(gè)總行為的方法。
基本方法:基本方法是實(shí)現(xiàn)算法各個(gè)步驟的方法,是模板方法的組成部分。基本方法如下:
?抽象方法(Abstract Method)
?具體方法(Concrete Method)
?鉤子方法(Hook Method):“掛鉤”方法和空方法,
hook方法在抽象類(lèi)中的實(shí)現(xiàn)為空,是留給子類(lèi)做一些可選的操作。如果某個(gè)子類(lèi)需要一些特殊額外的操作,則可以實(shí)現(xiàn)hook方法,當(dāng)然也可以完全不用理會(huì),因?yàn)閔ook在抽象類(lèi)中只是空方法而已。
1)鉤子方法的引入使得子類(lèi)可以控制父類(lèi)的行為。
2)最簡(jiǎn)單的鉤子方法就是空方法,也可以在鉤子方法中定義一個(gè)默認(rèn)的實(shí)現(xiàn),如果子類(lèi)不覆蓋鉤子方法,則執(zhí)行父類(lèi)的默認(rèn)實(shí)現(xiàn)代碼。
3)比較復(fù)雜一點(diǎn)的鉤子方法可以對(duì)其他方法進(jìn)行約束,這種鉤子方法通常返回一個(gè)boolean類(lèi)型,即返回true或false,用來(lái)判斷是否執(zhí)行某一個(gè)基本方法。由子類(lèi)來(lái)決定是否調(diào)用hook方法。
5) 小結(jié)
1)模板方法模式是一種類(lèi)的行為型模式,在它的結(jié)構(gòu)圖中只有類(lèi)之間的繼承關(guān)系,沒(méi)有對(duì)象關(guān)聯(lián)關(guān)系。
2)板方法模式是基于繼承的代碼復(fù)用基本技術(shù),模板方法模式的結(jié)構(gòu)和用法也是面向?qū)ο笤O(shè)計(jì)的核心之一。在模板方法模式中,可以將相同的代碼放在父類(lèi)中,而將不同的方法實(shí)現(xiàn)放在不同的子類(lèi)中。
3)在模板方法模式中,我們需要準(zhǔn)備一個(gè)抽象類(lèi),將部分邏輯以具體方法以及具體構(gòu)造函數(shù)的形式實(shí)現(xiàn),然后聲明一些抽象方法來(lái)讓子類(lèi)實(shí)現(xiàn)剩余的邏輯。不同的子類(lèi)可以以不同的方式實(shí)現(xiàn)這些抽象方法,從而對(duì)剩余的邏輯有不同的實(shí)現(xiàn),這就是模板方法模式的用意。模板方法模式體現(xiàn)了面向?qū)ο蟮闹T多重要思想,是一種使用頻率較高的模式。
?
總結(jié)
以上是生活随笔為你收集整理的C++设计模式之模板模式(template)(行为型)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 论文:图像分割的U-Net系列方法
- 下一篇: c++标准模板库:STL