模版模式,作為一種行為型模式,通過在抽象類或接口中定義一個(gè)操作中的算法骨架,而將一些步驟具體執(zhí)行延遲到子類中實(shí)現(xiàn),從而使得父類的方法執(zhí)行可以獲得不一樣的結(jié)果。從而達(dá)到了代碼復(fù)用、擴(kuò)展性好、靈活度高的設(shè)計(jì)目的。
使用時(shí)機(jī)
模版模式使用時(shí)機(jī),主要是相同、相似方法使用較多的情況。采用模版模式,可以將這些相似的方法提取出來,制定出一個(gè)相對普適的模版,從而減少代碼的重復(fù)書寫,提高代碼的復(fù)用率。抽象化的使用場景及實(shí)現(xiàn)邏輯如下圖所示。

基本定義
在了解如何寫模版模式之前,我們有必要來了解一些有關(guān)模版模式的關(guān)鍵名詞及術(shù)語。
基本方法:
基本方法又可以劃分為如下三類:
1、具體方法(Concrete Method)
具體方法指的是由父類/接口負(fù)責(zé)實(shí)現(xiàn)的方法,子類不可以對該方法進(jìn)行更改。
2、抽象方法(Abstract Method)
抽象方法則指的是不由父類/接口實(shí)現(xiàn)具體步驟,而是將其具體操作步驟推遲到子類實(shí)現(xiàn),從而對不同情況實(shí)現(xiàn)不同的操作。
3、鉤子方法(Hook Method)
一個(gè)鉤子方法由抽象類聲明并實(shí)現(xiàn),而子類會(huì)加以擴(kuò)展。**它是子類可以選擇性實(shí)現(xiàn)或不實(shí)現(xiàn)的方法**,通常抽象類給出是一個(gè)空實(shí)現(xiàn),作為方法的默認(rèn)實(shí)現(xiàn)。
這樣的默認(rèn)實(shí)現(xiàn),被稱為**默認(rèn)鉤子方法**。這種空鉤子方法也叫做“Do Nothing Hook”。默認(rèn)鉤子方法在缺省適配模式也有相應(yīng)的使用。缺省適配模式講的是一個(gè)類為接口提供一個(gè)默認(rèn)的空實(shí)現(xiàn),從而使得子類不必給出所有方法的實(shí)現(xiàn),因?yàn)橥ǔR粋€(gè)具體類并不需要所有的方法。這個(gè)思想正好同默認(rèn)鉤子方法不謀而合。
鉤子方法常見的用途為,將兩個(gè)存在不同調(diào)用關(guān)系的pipeLine流程,通過鉤子方法聯(lián)系到同一個(gè)模版中,從而屏蔽不同內(nèi)容的差異性。但是,需要注意的一點(diǎn)是,**鉤子方法的名字應(yīng)當(dāng)以do開始,這是熟悉設(shè)計(jì)模式的Java開發(fā)人員的標(biāo)準(zhǔn)做法。**如doScan、doGet等。
模版方法:
模版方法是模版模式的核心點(diǎn),其是定義在抽象類中的,是把基本操作方法組合在一起形成總的算法或行為的方法。一個(gè)抽象類可以有**任意多個(gè)模板方法**,而不限于一個(gè)。每一個(gè)模板方法都可以調(diào)用任意多個(gè)具體方法。原則上,**子類不可以、也不應(yīng)該對模版方法進(jìn)行覆蓋或者重寫**。
上述定義中各類方法的對應(yīng)模塊大致如下圖所示:

代碼實(shí)踐
模版模式的實(shí)現(xiàn),在java中有兩種方式,一種是基于抽象類的實(shí)現(xiàn)方式,另外一種則是基于接口的實(shí)現(xiàn)方式。
這里我們以抽象類的實(shí)現(xiàn)方法為例子介紹相應(yīng)的模版模式的實(shí)現(xiàn)。
public abstract class Template {
public void concreteMethod() {
System.out.println("concreteMethod:");
}
public abstract void abstractMethod();
public void hookMethod(){
System.out.println("hookMethod:");
System.out.println("實(shí)現(xiàn)默認(rèn)hookMethod!");
}
public final void execute() {
concreteMethod();
abstractMethod();
hookMethod();
}
}
首先,定義出我們的模版接口,其中包含三個(gè)方法concreteMethod、abstractMethod、hookMethod。就分別對應(yīng)于我們上述提到的具體方法、抽象方法及鉤子方法。
然后在execute()方法內(nèi),定義好三個(gè)方法的基本執(zhí)行方法,同時(shí)采用final修飾符,使得子類無法修改execute方法中的執(zhí)行順序。
然后在我們的子類HerFuction中,首先必須要對抽象方法進(jìn)行相應(yīng)的實(shí)現(xiàn)。這里我們簡單的輸出類名。
而在鉤子方法hookMethod中,我們則對原方法進(jìn)行增強(qiáng),多輸出一句話:“我還要執(zhí)行自己的hookMethod方法!”。
public class HerFunction implements Template {
@Override
public void abstractMethod() {
System.out.println("HerFunciton !");
}
@Override
public void hookMethod() {
Template.super.hookMethod();
System.out.println("我還要執(zhí)行自己的hookMethod方法!");
}
}
類似地,在第二個(gè)子類MyFunction中,我們也需要實(shí)現(xiàn)相應(yīng)的抽象類方法。但對于鉤子方法則采用默認(rèn)的抽象類中的方法實(shí)現(xiàn)即可。
public class MyFunction extends Template {
@Override
public void abstractMethod() {
System.out.println("My Function!");
}
}
最后,在啟動(dòng)方法中分別創(chuàng)建MyFunction、HerFunction對應(yīng)的對象,并調(diào)用父類的execute方法即可。
public static void main(String[] args) {
Template myFunction = new MyFunction();
myFunction.execute();
System.out.println("================我是分割線=========================");
Template herFunction = new HerFunction();
herFunction.execute();
}
最后得到的結(jié)果如下:

可以看到,子類對concreteMethod方法都成功實(shí)現(xiàn)了復(fù)用,而對于abstractMethod則根據(jù)不同子類實(shí)現(xiàn)了不同的邏輯,體現(xiàn)了差異性。同時(shí)鉤子方法的默認(rèn)實(shí)現(xiàn)及子類實(shí)現(xiàn),也體現(xiàn)了模版的靈活性。
優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn):
1、利用模板模式將相同處理邏輯的代碼放到抽象父類中,可以提高代碼的復(fù)用性。
2、將相同業(yè)務(wù)含義的處理代碼放置到不同的子類中,通過對子類的擴(kuò)展增加新的行為,從而提高代碼的擴(kuò)展性。
3、把不變的行為寫在父類上,去除子類的重復(fù)代碼,提供了一個(gè)很好的代碼復(fù)用平臺,符合開閉原則。
缺點(diǎn):
1、模版模式的實(shí)現(xiàn)依賴于子類的構(gòu)建,因此類的數(shù)量會(huì)存在明顯的增加,增加了類的復(fù)雜度。
2、采用抽象類情況下,繼承關(guān)系自身存在一定缺點(diǎn),如果父類添加新的抽象方法,所有子類都要對該抽象方法進(jìn)行實(shí)現(xiàn)。JAVA語言可以采用接口+default關(guān)鍵字的方式,一定程度上避免這個(gè)修改。(但是帶來的副作用是不能采用final對方法進(jìn)行限制)
參考文獻(xiàn)
《JAVA設(shè)計(jì)模式》之模板模式(Template)