JAVA設計模式--模板模式
引言
喜愛車的朋友們一定知道,今年tesla釋出了一款車叫model 3,這輛車支援兩種解鎖方式,一種是刷卡進入,還有一種使用手機解鎖,也就是說開走一輛model 3 的步驟如下:解鎖、馳騁、上鎖。如果你身邊有一群開的model 3的朋友,你們相約一起不停的解鎖、馳騁、上鎖,那場面一定很酷。
為什麼要使用模板模式
現在以model 3的開鎖方式來分,一般是以刷卡進入車內和手機解鎖兩種,正常的邏輯思維是抽象成一個父類,子類繼承父類,並實現抽象方法:

使用刷卡進入model 3的方式如下:

使用手機進入model3的方式如下:

OK, 看客戶端的呼叫:

相信都已經看出程式碼的問題,use方法的實現是一樣的,也就是程式碼重複了,這是病必須得治,藥方就是模板方式模式。
什麼是模板模式
模板模式的定義如下:

模板方法模式是一種基於繼承的程式碼複用技術,它是一種類行為型模式。 模板方法模式是結構最簡單的行為型設計模式,在其結構中只存在父類與子類之間的繼承關係。通過使用模板方法模式,可以將一些複雜流程的實現步驟封裝在一系列基本方法中,在抽象父類中提供一個稱之為模板方法的方法來定義這些基本方法的執行次序,而通過其子類來覆蓋某些步驟,從而使得相同的演算法框架可以有不同的執行結果。模板方法模式提供了一個模板方法來定義演算法框架,而某些具體步驟的實現可以在其子類中完成。
在軟體開發中,有時會遇到類似的情況,某個方法的實現需要多個步驟(類似“請客”),其中有些步驟是固定的(類似“點單”和“買單”),而有些步驟並不固定,存在可變性(類似“吃東西”)。為了提高程式碼的複用性和系統的靈活性,可以使用一種稱之為模板方法模式的設計模式來對這類情況進行設計,在模板方法模式中,將實現功能的每一個步驟所對應的方法稱為基本方法(例如“點單”、“吃東西”和“買單”),而呼叫這些基本方法同時定義基本方法的執行次序的方法稱為模板方法(例如“請客”)。在模板方法模式中,可以將相同的程式碼放在父類中,例如將模板方法“請客”以及基本方法“點單”和“買單”的實現放在父類中,而對於基本方法“吃東西”,在父類中只做一個宣告,將其具體實現放在不同的子類中,在一個子類中提供“吃麵條”的實現,而另一個子類提供“吃滿漢全席”的實現。通過使用模板方法模式,一方面提高了程式碼的複用性,另一方面還可以利用面向物件的多型性,在執行時選擇一種具體子類,實現完整的“請客”方法,提高系統的靈活性和可擴充套件性。
模板方法模式結構與實現
模式結構
模板方法模式結構比較簡單,其核心是抽象類和其中的模板方法的設計,其結構如圖所示:

模板方法模式包含如下兩個角色:
AbstractClass(抽象類)
在抽象類中定義了一系列基本操作(PrimitiveOperations),這些基本操作可以是具體的,也可以是抽象的,每一個基本操作對應演算法的一個步驟,在其子類中可以重定義或實現這些步驟。同時,在抽象類中實現了一個模板方法(Template Method),用於定義一個演算法的框架,模板方法不僅可以呼叫在抽象類中實現的基本方法,也可以呼叫在抽象類的子類中實現的基本方法,還可以呼叫其他物件中的方法。
ConcreteClass(具體子類)
它是抽象類的子類,用於實現在父類中宣告的抽象基本操作以完成子類特定演算法的步驟,也可以覆蓋在父類中已經實現的具體基本操作。
模式實現
在實現模板方法模式時,開發抽象類的軟體設計師和開發具體子類的軟體設計師之間可以進行協作。一個設計師負責給出一個演算法的輪廓和框架,另一些設計師則負責給出這個演算法的各個邏輯步驟。實現這些具體邏輯步驟的方法即為基本方法,而將這些基本方法彙總起來的方法即為模板方法,模板方法模式的名字也因此而來。下面將詳細介紹模板方法和基本方法:
模板方法
一個模板方法是定義在抽象類中的、把基本操作方法組合在一起形成一個總演算法或一個總行為的方法。這個模板方法定義在抽象類中,並由子類不加以修改地完全繼承下來。模板方法是一個具體方法,它給出了一個頂層邏輯框架,而邏輯的組成步驟在抽象類中可以是具體方法,也可以是抽象方法。由於模板方法是具體方法,因此模板方法模式中的抽象層只能是抽象類,而不是介面。
基本方法
基本方法是實現演算法各個步驟的方法,是模板方法的組成部分。基本方法又可以分為三種:抽象方法(Abstract Method)、具體方法(Concrete Method)和鉤子方法(Hook Method)。 (1) 抽象方法:一個抽象方法由抽象類宣告、由其具體子類實現。抽象方法以abstract關鍵字標識。 (2) 具體方法:一個具體方法由一個抽象類或具體類宣告並實現,其子類可以進行覆蓋也可以直接繼承。 (3) 鉤子方法:一個鉤子方法由一個抽象類或具體類宣告並實現,而其子類可能會加以擴充套件。通常在父類中給出的實現是一個空實現,並以該空實現作為方法的預設實現,當然鉤子方法也可以提供一個非空的預設實現。
在模板方法模式中,鉤子方法有兩類:第一類鉤子方法可以與一些具體步驟“掛鉤”,以實現在不同條件下執行模板方法中的不同步驟,這類鉤子方法的返回型別通常是bool型別的,這類方法名一般為IsXXX(),用於對某個條件進行判斷,如果條件滿足則執行某一步驟,否則將不執行,如下程式碼片段所示:

在程式碼中isPrint()方法即是鉤子方法,它可以決定print()方法是否執行,一般情況下,鉤子方法的返回值為true,如果不希望某方法執行,可以在其子類中覆蓋鉤子方法,將其返回值改為false即可,這種型別的鉤子方法可以控制方法的執行,對一個演算法進行約束。
還有一類鉤子方法就是實現體為空的具體方法,子類可以根據需要覆蓋或者繼承這些鉤子方法,與抽象方法相比,這類鉤子方法的好處在於子類如果沒有覆蓋父類中定義的鉤子方法,編譯可以正常通過,但是如果沒有覆蓋父類中宣告的抽象方法,編譯將報錯。
案例
OK,學習了模板模式後,我們將前面的model 3的例子使用模板模式改寫一下.
第一步還是定義我們的父類,一些共同的方法,我們直接在父類實現,如下:

使用刷卡進入model 3的方式如下:

使用手機進入model 3的方式如下:

客戶端的呼叫不變,我們發現在子類中只是實現瞭解鎖的方式不一樣,其他步驟和父類保持不變,消除了重複程式碼。
總結
在模板方法模式中,由於面向物件的多型性,子類物件在執行時將覆蓋父類物件,子類中定義的方法也將覆蓋父類中定義的方法,因此程式在執行時,具體子類的基本方法將覆蓋父類中定義的基本方法,子類的鉤子方法也將覆蓋父類的鉤子方法,從而可以通過在子類中實現的鉤子方法對父類方法的執行進行約束,實現子類對父類行為的反向控制。