15.模板方法模式Template Method

1.初識(shí)模板方法模式

定義一個(gè)操作中的算法的骨架瓢宦,而將一些步驟延遲到子類(lèi)中疙赠。模板方法使得子類(lèi)可以不改變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟诅福。

  • AbstractClass:抽象類(lèi)。用來(lái)定義算法骨架和原語(yǔ)操作让虐,在這個(gè)類(lèi)里面,還可以提供算法中通用的實(shí)現(xiàn)罢荡。
    ConcreteClass:具體實(shí)現(xiàn)類(lèi)赡突。用來(lái)實(shí)現(xiàn)算法骨架中的某些步驟,完成跟特定子類(lèi)相關(guān)的功能区赵。

2.體會(huì)模板方法模式

2.1 場(chǎng)景問(wèn)題——登錄控制

先看看普通用戶(hù)登錄前臺(tái)的登錄控制的功能

  • 1)前臺(tái)頁(yè)面:用戶(hù)能輸入用戶(hù)名和密碼惭缰;提交登錄請(qǐng)求,讓系統(tǒng)去進(jìn)行登錄控制
  • 2)后臺(tái):從數(shù)據(jù)庫(kù)獲取登錄人員的信息
  • 3)后臺(tái):判斷從前臺(tái)傳遞過(guò)來(lái)的登錄數(shù)據(jù)笼才,和數(shù)據(jù)庫(kù)中已有的數(shù)據(jù)是否匹配
  • 4)前臺(tái)Action:如果匹配就轉(zhuǎn)向首頁(yè)漱受,如果不匹配就返回到登錄頁(yè)面,并顯示錯(cuò)誤提示信息

再來(lái)看看工作人員登錄后臺(tái)的登錄控制功能:

  • 1)前臺(tái)頁(yè)面:用戶(hù)能輸入用戶(hù)名和密碼骡送;提交登錄請(qǐng)求拜效,讓系統(tǒng)去進(jìn)行登錄控制
  • 2)后臺(tái):從數(shù)據(jù)庫(kù)獲取登錄人員的信息
  • 3)后臺(tái):把從前臺(tái)傳遞過(guò)來(lái)的密碼數(shù)據(jù),使用相應(yīng)的加密算法進(jìn)行加密運(yùn)算各谚,得到加密后的
    密碼數(shù)據(jù)
  • 4)后臺(tái):判斷從前臺(tái)傳遞過(guò)來(lái)的用戶(hù)名和加密后的密碼數(shù)據(jù)紧憾,和數(shù)據(jù)庫(kù)中已有的數(shù)據(jù)是否匹配
  • 5)前臺(tái)Action:如果匹配就轉(zhuǎn)向首頁(yè),如果不匹配就返回到登錄頁(yè)面昌渤,并顯示錯(cuò)誤提示信息

2.2 不用模式的解決方案

有何問(wèn)題:
看了這里的實(shí)現(xiàn)示例赴穗,是不是很簡(jiǎn)單。但是膀息,仔細(xì)看看般眉,總會(huì)覺(jué)得有點(diǎn)問(wèn)題,兩種登錄的實(shí)現(xiàn)太相似了潜支,現(xiàn)在是完全分開(kāi)甸赃,當(dāng)作兩個(gè)獨(dú)立的模塊來(lái)實(shí)現(xiàn)的,如果今后要擴(kuò)展功能冗酿,比如要添加“控制同一個(gè)編號(hào)同時(shí)只能登錄一次”的功能埠对,那么兩個(gè)模塊都需要修改络断,是很麻煩的。而且项玛,現(xiàn)在的實(shí)現(xiàn)中貌笨,也有很多相似的地方,顯得很重復(fù)襟沮。另外锥惋,具體的實(shí)現(xiàn)和判斷的步驟混合在一起,不利于今后變換功能开伏,比如要變換加密算法等膀跌。

總之,上面的實(shí)現(xiàn)固灵,有兩個(gè)很明顯的問(wèn)題:一是重復(fù)或相似代碼太多捅伤;二是擴(kuò)展起來(lái)很不方便。

那么該怎么解決呢怎虫?該如何實(shí)現(xiàn)才能讓系統(tǒng)既靈活又能簡(jiǎn)潔的實(shí)現(xiàn)需求功能呢暑认?

2.3 使用模式的解決方案

3.理解模板方法模式

3.1 認(rèn)識(shí)模板方法模式

3.1.1 模式的功能

模板方法的功能在于固定算法骨架,而讓具體算法實(shí)現(xiàn)可擴(kuò)展大审。

這在實(shí)際應(yīng)用中非常廣泛蘸际,尤其是在設(shè)計(jì)框架級(jí)功能的時(shí)候非常有用⊥椒觯框架定義好了算法的步驟粮彤,在合適的點(diǎn)讓開(kāi)發(fā)人員進(jìn)行擴(kuò)展,實(shí)現(xiàn)具體的算法姜骡。比如在DAO實(shí)現(xiàn)中导坟,設(shè)計(jì)通用的增刪改查功能,這個(gè)后面會(huì)給大家示例圈澈。

模板方法還額外提供了一個(gè)好處惫周,就是可以控制子類(lèi)的擴(kuò)展。因?yàn)樵诟割?lèi)里面定義好了算法的步驟康栈,只是在某幾個(gè)固定的點(diǎn)才會(huì)調(diào)用到被子類(lèi)實(shí)現(xiàn)的方法递递,因此也就只允許在這幾個(gè)點(diǎn)來(lái)擴(kuò)展功能,這些個(gè)可以被子類(lèi)覆蓋以擴(kuò)展功能的方法通常被稱(chēng)為“鉤子”方法啥么,后面也會(huì)給大家示例登舞。

3.1.2 為何不是接口

首先搞清楚抽象類(lèi)和接口的關(guān)系。

其次要明了什么時(shí)候使用抽象類(lèi)悬荣,那就是通常在“既要約束子類(lèi)的行為菠秒,又要為子類(lèi)提供公共功能”的時(shí)候使用抽象類(lèi)。

按照這個(gè)原則來(lái)思考模板方法模式的實(shí)現(xiàn)氯迂,模板方法模式需要固定定義算法的骨架践叠,這個(gè)骨架應(yīng)該只有一份言缤,算是一個(gè)公共的行為,但是里面具體的步驟的實(shí)現(xiàn)又可能是各不相同的酵熙,恰好符合選擇抽象類(lèi)的原則轧简。

把模板實(shí)現(xiàn)成為抽象類(lèi)驰坊,為所有的子類(lèi)提供了公共的功能匾二,就是定義了具體的算法骨架;同時(shí)在模板里面把需要由子類(lèi)擴(kuò)展的具體步驟的算法定義成為抽象方法拳芙,要求子類(lèi)去實(shí)現(xiàn)這些方法察藐,這就約束了子類(lèi)的行為。

因此綜合考慮舟扎,用抽象類(lèi)來(lái)實(shí)現(xiàn)模板是一個(gè)很好的選擇分飞。

3.1.3 變與不變

程序設(shè)計(jì)的一個(gè)很重要的思考點(diǎn)就是“變與不變”,也就是分析程序中哪些功能是可變的睹限,哪些功能是不變的譬猫,把不變的部分抽象出來(lái),進(jìn)行公共的實(shí)現(xiàn)羡疗,把變化的部分分離出去染服,用接口來(lái)封裝隔離,或用抽象類(lèi)來(lái)約束子類(lèi)行為叨恨。

模板方法模式很好的體現(xiàn)了這一點(diǎn)柳刮。模板類(lèi)實(shí)現(xiàn)的就是不變的方法和算法的骨架,而需要變化的地方痒钝,都通過(guò)抽象方法秉颗,把具體實(shí)現(xiàn)延遲到子類(lèi),還通過(guò)父類(lèi)的定義來(lái)約束了子類(lèi)的行為送矩,從而使系統(tǒng)能有更好的復(fù)用性和擴(kuò)展性蚕甥。

3.1.4 好萊塢法則

什么是好萊塢法則呢?簡(jiǎn)單點(diǎn)說(shuō)栋荸,就是“不要找我們菇怀,我們會(huì)聯(lián)系你”。

模板方法模式很好的體現(xiàn)了這一點(diǎn)蒸其,做為父類(lèi)的模板會(huì)在需要的時(shí)候敏释,調(diào)用子類(lèi)相應(yīng)的方法,也就是由父類(lèi)來(lái)找子類(lèi)摸袁,而不是讓子類(lèi)來(lái)找父類(lèi)钥顽。

這是一種反向的控制結(jié)構(gòu),按照通常的思路靠汁,是子類(lèi)找父類(lèi)才對(duì)蜂大,也就是應(yīng)該是子類(lèi)來(lái)調(diào)用父類(lèi)的方法闽铐,因?yàn)楦割?lèi)根本就不知道子類(lèi),而子類(lèi)是知道父類(lèi)的奶浦,但是在模板方法模式里面兄墅,是父類(lèi)來(lái)找子類(lèi),所以是一種反向的控制結(jié)構(gòu)澳叉。

在Java里面能實(shí)現(xiàn)這樣功能的理論依據(jù)在哪里呢隙咸?
理論依據(jù)就在于Java的動(dòng)態(tài)綁定采用的是“后期綁定”技術(shù),對(duì)于出現(xiàn)子類(lèi)覆蓋父類(lèi)方法的情況成洗,在編譯時(shí)是看數(shù)據(jù)類(lèi)型五督,運(yùn)行時(shí)看實(shí)際的對(duì)象類(lèi)型(new操作符后跟的構(gòu)造方法是哪個(gè)類(lèi)的),一句話:new誰(shuí)就調(diào)用誰(shuí)的方法瓶殃。

因此在使用模板方法模式的時(shí)候充包,雖然用的數(shù)據(jù)類(lèi)型是模板類(lèi)型,但是在創(chuàng)建類(lèi)實(shí)例的時(shí)候是創(chuàng)建的具體的子類(lèi)的實(shí)例遥椿,因此調(diào)用的時(shí)候基矮,會(huì)被動(dòng)態(tài)綁定到子類(lèi)的方法上去,從而實(shí)現(xiàn)反向控制冠场。其實(shí)在寫(xiě)父類(lèi)的時(shí)候家浇,它調(diào)用的方法是父類(lèi)自己的抽象方法,只是在運(yùn)行的時(shí)候被動(dòng)態(tài)綁定到了子類(lèi)的方法上慈鸠。

3.1.5 擴(kuò)展登錄控制

在使用模板方法模式實(shí)現(xiàn)過(guò)后蓝谨,如果想要擴(kuò)展新的功能,有如下幾種情況:

  • 1)一種情況是只需要提供新的子類(lèi)實(shí)現(xiàn)就可以了青团,比如想要切換不同的加密算法譬巫,現(xiàn)在是使用的MD5,想要實(shí)現(xiàn)使用 3DES的加密算法督笆,那就新做一個(gè)子類(lèi)芦昔,然后覆蓋實(shí)現(xiàn)父類(lèi)加密的方法,在里面使用3DES來(lái)實(shí)現(xiàn)即可娃肿,已有的實(shí)現(xiàn)不需要做任何變化
  • 2)另外一種情況是想要給兩個(gè)登錄模塊都擴(kuò)展同一個(gè)功能咕缎,這種情況多屬于需要修改模板方法的算法骨架的情況,應(yīng)該盡量避免料扰,但是萬(wàn)一前面沒(méi)有考慮周全凭豪,后來(lái)出現(xiàn)了這種情況,怎么辦呢晒杈?最好就是重構(gòu)嫂伞,也就是考慮修改算法骨架,盡量不要去找其它的替代方式,替代的方式也許能把功能實(shí)現(xiàn)了帖努,但是會(huì)破壞整個(gè)程序的結(jié)構(gòu)撰豺。
  • 3)還有一種情況是既需要加入新的功能,也需要新的數(shù)據(jù)拼余。比如:現(xiàn)在對(duì)于普通人員登錄污桦,要實(shí)現(xiàn)一個(gè)加強(qiáng)版,要求登錄人員除了編號(hào)和密碼外匙监,還需要提供注冊(cè)時(shí)留下的驗(yàn)證問(wèn)題和驗(yàn)證答案凡橱,驗(yàn)證問(wèn)題和驗(yàn)證答案是記錄在數(shù)據(jù)庫(kù)中的,不是驗(yàn)證碼舅柜,一般Web開(kāi)發(fā)中登錄使用的驗(yàn)證碼會(huì)放到session中梭纹,這里不去討論它躲惰。

3.2 模板的寫(xiě)法

通常在模板里面包含如下操作類(lèi)型:

  • 1)模板方法:就是定義算法骨架的方法
  • 2)具體的操作:在模板中直接實(shí)現(xiàn)某些步驟的方法致份,通常這些步驟的實(shí)現(xiàn)算法是固定的,而且是不怎么變化的础拨,因此就可以當(dāng)作公共功能實(shí)現(xiàn)在模板里面氮块。如果不需提供給子類(lèi)訪問(wèn)這些方法的話,還可以是private的诡宗。這樣一來(lái)滔蝉,子類(lèi)的實(shí)現(xiàn)就相對(duì)簡(jiǎn)單些。如果是子類(lèi)需要訪問(wèn)塔沃,可以把這些方法定義為protected final的蝠引,因?yàn)橥ǔG闆r下,這些實(shí)現(xiàn)不能夠被子類(lèi)覆蓋和改變了蛀柴。
  • 3)具體的AbstractClass操作:在模板中實(shí)現(xiàn)某些公共功能螃概,可以提供給子類(lèi)使用,一般不是具體的算法步驟的實(shí)現(xiàn)鸽疾,只是一些輔助的公共功能吊洼。
  • 4)原語(yǔ)操作:就是在模板中定義的抽象操作,通常是模板方法需要調(diào)用的操作制肮,是必需的操作冒窍,而且在父類(lèi)中還沒(méi)有辦法確定下來(lái)如何實(shí)現(xiàn),需要子類(lèi)來(lái)真正實(shí)現(xiàn)的方法豺鼻。
  • 5)鉤子操作:在模板中定義综液,并提供默認(rèn)實(shí)現(xiàn)的操作。這些方法通常被視為可擴(kuò)展的點(diǎn)儒飒,但不是必須的谬莹,子類(lèi)可以有選擇的覆蓋這些方法,以提供新的實(shí)現(xiàn)來(lái)擴(kuò)展功能。比如:模板方法中定義了5步操作届良,但是根據(jù)需要笆凌,某一種具體的實(shí)現(xiàn)只需要其中的1、2士葫、3這幾個(gè)步驟乞而,因此它就只需要覆蓋實(shí)現(xiàn)1、2慢显、3這幾個(gè)步驟對(duì)應(yīng)的方法爪模。那么4和5步驟對(duì)應(yīng)的方法怎么辦呢,由于有默認(rèn)實(shí)現(xiàn)荚藻,那就不用管了屋灌。也就是說(shuō)鉤子操作是可以被擴(kuò)展的點(diǎn),但不是必須的应狱。
  • 6)Factory Method:在模板方法中共郭,如果需要得到某些對(duì)象實(shí)例的話,可以考慮通過(guò)工廠方法模式來(lái)獲取疾呻,把具體的構(gòu)建對(duì)象的實(shí)現(xiàn)延遲到子類(lèi)中去除嘹。

3.3 Java回調(diào)與模板方法模式

模板方法模式的一個(gè)目的,就在于讓其它類(lèi)來(lái)擴(kuò)展或具體實(shí)現(xiàn)在模板中固定的算法骨架中的某些算法步驟岸蜗。在標(biāo)準(zhǔn)的模板方法模式實(shí)現(xiàn)中尉咕,主要是使用繼承的方式,來(lái)讓父類(lèi)在運(yùn)行期間可以調(diào)用到子類(lèi)的方法璃岳。

其實(shí)在Java開(kāi)發(fā)中年缎,還有另外一個(gè)方法可以實(shí)現(xiàn)同樣的功能或是效果,那就是——Java回調(diào)技術(shù)铃慷,通過(guò)回調(diào)在接口中定義的方法单芜,調(diào)用到具體的實(shí)現(xiàn)類(lèi)中的方法,其本質(zhì)同樣是利用Java的動(dòng)態(tài)綁定技術(shù)枚冗,在這種實(shí)現(xiàn)中缓溅,可以不把實(shí)現(xiàn)類(lèi)寫(xiě)成單獨(dú)的類(lèi),而是使用匿名內(nèi)部類(lèi)來(lái)實(shí)現(xiàn)回調(diào)方法赁温。

**兩種實(shí)現(xiàn)方式的比較 **

  • 1)使用繼承的方式坛怪,抽象方法和具體實(shí)現(xiàn)的關(guān)系,是在編譯期間靜態(tài)決定的股囊,是類(lèi)級(jí)關(guān)系袜匿;使用Java回調(diào),這個(gè)關(guān)系是在運(yùn)行期間動(dòng)態(tài)決定的稚疹,是對(duì)象級(jí)的關(guān)系居灯。
  • 2)相對(duì)而言祭务,使用回調(diào)機(jī)制會(huì)更靈活,因?yàn)镴ava是單繼承的怪嫌,如果使用繼承的方式义锥,對(duì)于子類(lèi)而言,今后就不能繼承其它對(duì)象了岩灭,而使用回調(diào)拌倍,是基于接口的。
    從另一方面說(shuō)噪径,回調(diào)機(jī)制是通過(guò)委托的方式來(lái)組合功能柱恤,它的耦合強(qiáng)度要比繼承低一些,這會(huì)給我們更多的靈活性找爱。比如某些模板實(shí)現(xiàn)的方法梗顺,在回調(diào)實(shí)現(xiàn)的時(shí)候可以不調(diào)用模板中的方法,而是調(diào)用其它實(shí)現(xiàn)中的某些功能车摄,也就是說(shuō)功能不再局限在模板和回調(diào)實(shí)現(xiàn)上了寺谤,可以更靈活組織功能。
  • 3)相對(duì)而言练般,使用繼承方式會(huì)更簡(jiǎn)單點(diǎn)矗漾,因?yàn)楦割?lèi)提供了實(shí)現(xiàn)的方法,子類(lèi)如果不想擴(kuò)展薄料,那就不用管。如果使用回調(diào)機(jī)制泵琳,回調(diào)的接口需要把所有可能被擴(kuò)展的方法都定義進(jìn)去摄职,這就導(dǎo)致實(shí)現(xiàn)的時(shí)候,不管你要不要擴(kuò)展获列,你都要實(shí)現(xiàn)這個(gè)方法谷市,哪怕你什么都不做,只是轉(zhuǎn)調(diào)模板中已有的實(shí)現(xiàn)击孩,都要寫(xiě)出來(lái)迫悠。
    事實(shí)上,在前面講命令模式的時(shí)候也提到了Java回調(diào)巩梢,還通過(guò)退化命令模式來(lái)實(shí)現(xiàn)了Java回調(diào)的功能创泄,所以也有這樣的說(shuō)法:命令模式可以作為模板方法模式的一種替代實(shí)現(xiàn),那就是因?yàn)榭梢允褂肑ava回調(diào)來(lái)實(shí)現(xiàn)模板方法模式括蝠。

3.4 典型應(yīng)用:排序

模板方法模式的一個(gè)非常典型的應(yīng)用鞠抑,就是實(shí)現(xiàn)排序的功能。

在java.util包中忌警,有一個(gè)Collections類(lèi)搁拙,它里面實(shí)現(xiàn)了對(duì)列表排序的功
能,它提供了一個(gè)靜態(tài)的sort方法,接受一個(gè)列表和一個(gè)Comparator接口的實(shí)例箕速,這個(gè)方法實(shí)現(xiàn)的大致步驟是:

  • step1.先把列表轉(zhuǎn)換成為對(duì)象數(shù)組
  • step2.通過(guò)Arrays的sort方法來(lái)對(duì)數(shù)組進(jìn)行排序酪碘,傳入Comparator接口的實(shí)例
  • step3.然后再把排好序的數(shù)組的數(shù)據(jù)設(shè)置回到原來(lái)的列表對(duì)象中去

這其中的算法步驟是固定的,也就是算法骨架是固定的了盐茎,只是其中具體比較數(shù)據(jù)大小的步驟婆跑,需要由外部來(lái)提供,也就是傳入的Comparator接口的實(shí)例庭呜,就是用來(lái)實(shí)現(xiàn)數(shù)據(jù)比較的滑进,在算法內(nèi)部會(huì)通過(guò)這個(gè)接口來(lái)回調(diào)具體的實(shí)現(xiàn)。

排序募谎,到底是模板方法模式扶关,還是策略模式的實(shí)例,到底哪個(gè)說(shuō)法更合適数冬?

1.認(rèn)為是策略模式的實(shí)例的理由:

  • 1)首先上面的排序?qū)崿F(xiàn)节槐,并沒(méi)有如同標(biāo)準(zhǔn)的模板方法模式那樣,使用子類(lèi)來(lái)擴(kuò)展父
    類(lèi)拐纱,至少?gòu)谋砻嫔峡床惶衲0宸椒J剑?/li>
  • 2)其次排序使用的 Comparator的實(shí)例铜异,可以看成是不同的算法實(shí)現(xiàn),在具體排序時(shí)秸架,會(huì)選擇使用不同的Comparator實(shí)現(xiàn)揍庄,就相當(dāng)于是在切換算法的實(shí)現(xiàn)。

2.認(rèn)為是模板方法模式的實(shí)例的理由:

  • 1)首先东抹,模板方法模式的本質(zhì)是固定算法骨架蚂子,雖然使用繼承是標(biāo)準(zhǔn)的實(shí)現(xiàn)方式,但是通過(guò)回調(diào)來(lái)實(shí)現(xiàn)缭黔,也不能說(shuō)這就不是模板方法模式食茎;
  • 2)其次,從整體程序上看馏谨,排序的算法并沒(méi)有改變别渔,不過(guò)是某些步驟的實(shí)現(xiàn)發(fā)生了變化,也就是說(shuō)通過(guò)Comparator來(lái)切換的是不同的比較大小的實(shí)現(xiàn)惧互,相對(duì)于整個(gè)排序算法而言哎媚,它不過(guò)是其中的一個(gè)步驟而已。

總結(jié):排序的實(shí)現(xiàn)壹哺,實(shí)際上組合使用了模板方法模式和策略模式抄伍,從整體來(lái)看是模板方法模式,但到了局部管宵,比如排序比較算法的實(shí)現(xiàn)上截珍,就是使用的策略模式了攀甚。

3.5 實(shí)現(xiàn)通用增刪改查

為了突出主題,以免分散大家的注意力岗喉,我們不去使用Spring和Hibernate這樣的流行框架秋度,也不去使用泛型,只用模板方法模式來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的钱床、用 JDBC實(shí)現(xiàn)的通用增刪改查的功能荚斯。

先在數(shù)據(jù)庫(kù)中定義一個(gè)演示用的表,演示用的是Oracle數(shù)據(jù)庫(kù)查牌,其實(shí)你可以用任意的數(shù)據(jù)庫(kù)事期,只是數(shù)據(jù)類(lèi)型要做相應(yīng)的調(diào)整,簡(jiǎn)單的數(shù)據(jù)字典如下:


3.6 模板方法模式的優(yōu)缺點(diǎn)

  • 實(shí)現(xiàn)代碼復(fù)用
  • 算法骨架不容易升級(jí)

4.思考模板方法模式

4.1 模板方法模式的本質(zhì)

模板方法模式的本質(zhì)是:固定算法骨架

4.2 對(duì)設(shè)計(jì)原則的體現(xiàn)

模板方法很好的體現(xiàn)了開(kāi)閉原則和里氏替換原則纸颜。

首先從設(shè)計(jì)上兽泣,先分離變與不變,然后把不變的部分抽取出來(lái)胁孙,定義到父類(lèi)里面唠倦,比如算法骨架,比如一些公共的涮较、固定的實(shí)現(xiàn)等等稠鼻。這些不變的部分被封閉起來(lái),盡量不去修改它了狂票,要擴(kuò)展新的功能候齿,那就使用子類(lèi)來(lái)擴(kuò)展,通過(guò)子類(lèi)來(lái)實(shí)現(xiàn)可變化的步驟苫亦,對(duì)于這種新增功能的做法是開(kāi)放的毛肋。

其次,能夠?qū)崿F(xiàn)統(tǒng)一的算法骨架屋剑,通過(guò)切換不同的具體實(shí)現(xiàn)來(lái)切換不同的功能,一個(gè)根本原因就是里氏替換原則诗眨,遵循這個(gè)原則唉匾,保證所有的子類(lèi)實(shí)現(xiàn)的是同一個(gè)算法模板,并能在使用模板的地方匠楚,根據(jù)需要巍膘,切換不同的具體實(shí)現(xiàn)。

4.3 何時(shí)選用

  • 1)需要固定定義算法骨架芋簿,實(shí)現(xiàn)一個(gè)算法的不變的部分峡懈,并把可變的行為留給子類(lèi)來(lái)實(shí)現(xiàn)的情況。
  • 2)各個(gè)子類(lèi)中具有公共行為与斤,應(yīng)該抽取出來(lái)肪康,集中在一個(gè)公共類(lèi)中去實(shí)現(xiàn)荚恶,從而避免代碼重復(fù)
  • 3)需要控制子類(lèi)擴(kuò)展的情況。模板方法模式會(huì)在特定的點(diǎn)來(lái)調(diào)用子類(lèi)的方法磷支,這樣只允許在這些點(diǎn)進(jìn)行擴(kuò)展

5.案例

5.1 Servlet中的模板方法

開(kāi)發(fā)web應(yīng)用或api接口谒撼,我們必然要用到servlet,一般步驟是寫(xiě)一個(gè)類(lèi)繼承HttpServlet 雾狈,然后重寫(xiě)doGet或者doPost方法來(lái)分別處理get請(qǐng)求和post請(qǐng)求:

public class Test extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("處理Get請(qǐng)求");
        super.doGet(req, resp);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("處理Post請(qǐng)求");
        super.doPost(req, resp);
    }
}

其實(shí)這也就是用了模板方法廓潜,我們打開(kāi)HttpServlet類(lèi)的源碼,發(fā)現(xiàn)除了doGet善榛,doPost外還有很多類(lèi)似的方法辩蛋,如doDelete,doPut等移盆。有一個(gè)共通點(diǎn)悼院,它們都是在service()方法中被調(diào)用:

    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        HttpServletRequest request;
        HttpServletResponse response;
        try {
            request = (HttpServletRequest)req;
            response = (HttpServletResponse)res;
        } catch (ClassCastException var6) {
            throw new ServletException("non-HTTP request or response");
        }

        this.service(request, response);
    }
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        long lastModified;
        if (method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader("If-Modified-Since");
                } catch (IllegalArgumentException var9) {
                    ifModifiedSince = -1L;
                }

                if (ifModifiedSince < lastModified / 1000L * 1000L) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if (method.equals("POST")) {
            this.doPost(req, resp);
        } else if (method.equals("PUT")) {
            this.doPut(req, resp);
        } else if (method.equals("DELETE")) {
            this.doDelete(req, resp);
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if (method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }

    }

service() 就是模板方法,doGet等方法就是需要子類(lèi)去實(shí)現(xiàn)的方法味滞。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末樱蛤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子剑鞍,更是在濱河造成了極大的恐慌昨凡,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蚁署,死亡現(xiàn)場(chǎng)離奇詭異便脊,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)光戈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)哪痰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人久妆,你說(shuō)我怎么就攤上這事晌杰。” “怎么了筷弦?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵肋演,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我烂琴,道長(zhǎng)爹殊,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任奸绷,我火速辦了婚禮梗夸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘号醉。我一直安慰自己反症,他們只是感情好辛块,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著惰帽,像睡著了一般憨降。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上该酗,一...
    開(kāi)封第一講書(shū)人閱讀 51,245評(píng)論 1 299
  • 那天授药,我揣著相機(jī)與錄音,去河邊找鬼呜魄。 笑死悔叽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的爵嗅。 我是一名探鬼主播娇澎,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼睹晒!你這毒婦竟也來(lái)了趟庄?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤伪很,失蹤者是張志新(化名)和其女友劉穎戚啥,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體锉试,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡猫十,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了呆盖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拖云。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖应又,靈堂內(nèi)的尸體忽然破棺而出宙项,到底是詐尸還是另有隱情,我是刑警寧澤株扛,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布杉允,位于F島的核電站,受9級(jí)特大地震影響席里,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拢驾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一奖磁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧繁疤,春花似錦咖为、人聲如沸秕狰。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鸣哀。三九已至,卻和暖如春吞彤,著一層夾襖步出監(jiān)牢的瞬間我衬,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工饰恕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留挠羔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓埋嵌,卻偏偏與公主長(zhǎng)得像破加,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子雹嗦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容