定義:
定義一個操作中的演算法的架構,而將一些步驟延遲到子類中。使得子類可以不改變一個演算法的結構即可重新定義該演算法的某些特定步驟。
聽起來好高端的樣子,我的理解:
1.父類聲明了若干個抽象方法(基本方法)和若干個具體方法(模板方法)
2.抽象方法是一個演算法(過程)的步驟,在子類中實現
3.模板方法是一個演算法(過程)的架構,在父類中已經約定好,實現對基本方法調用,完成固定的邏輯
4.一個演算法(過程)的結構在父類中定義,具體的實現細節則在子類中實現
註:為了防止惡意操作,一般模板方法都加上final,禁止重寫
通用類圖:
事實上,模版方法是編程中一個經常用到的模式。先來看一個例子,某日,程式員A拿到一個任務:給定一個整數數組,把數組中的數由小到大排序,然後把排序之後的結果列印出來。經過分析之後,這個任務大體上可分為兩部分,排序和列印,列印功能好實現,排序就有點麻煩了。但是A有辦法,先把列印功能完成,排序功能另找人做。
abstract class AbstractSort { /** * 將數組array由小到大排序 * @param array */ protected abstract void sort(int[] array); public void showSortResult(int[] array){ this.sort(array); System.out.print("排序結果:"); for (int i = 0; i < array.length; i++){ System.out.printf("%3s", array[i]); } } }
寫完後,A找到剛畢業入職不久的同事B說:有個任務,主要邏輯我已經寫好了,你把剩下的邏輯實現一下吧。於是把AbstractSort類給B,讓B寫實現。B拿過來一看,太簡單了,10分鐘搞定,代碼如下:
class ConcreteSort extends AbstractSort { @Override protected void sort(int[] array){ for(int i=0; i<array.length-1; i++){ selectSort(array, i); } } private void selectSort(int[] array, int index) { int MinValue = 32767; // 最小值變數 int indexMin = 0; // 最小值索引變數 int Temp; // 暫存變數 for (int i = index; i < array.length; i++) { if (array[i] < MinValue){ // 找到最小值 MinValue = array[i]; // 儲存最小值 indexMin = i; } } Temp = array[index]; // 交換兩數值 array[index] = array[indexMin]; array[indexMin] = Temp; } }
寫好後交給A,A拿來一運行:
public class Client { public static int[] a = { 10, 32, 1, 9, 5, 7, 12, 0, 4, 3 }; // 預設資料數組 public static void main(String[] args){ AbstractSort s = new ConcreteSort(); s.showSortResult(a); } }
運行結果:
排序結果: 0 1 3 4 5 7 9 10 12 32
模版方法模式的結構
模版方法模式由一個抽象類別和一個(或一組)實作類別通過繼承結構組成,抽象類別中的方法分為三種:
抽象方法:父類中只聲明但不加以實現,而是定義好規範,然後由它的子類去實現。
模版方法:由抽象類別聲明並加以實現。一般來說,模版方法調用抽象方法來完成主要的邏輯功能,並且,模版方法大多會定義為final類型,指明主要的邏輯功能在子類中不能被重寫。
鉤子方法:由抽象類別聲明並加以實現。但是子類可以去擴充,子類可以通過擴充鉤子方法來影響模版方法的邏輯。
抽象類別的任務是搭建邏輯的架構,通常由經驗豐富的人員編寫,因為抽象類別的好壞直接決定了程式是否穩定性。
實作類別用來實現細節。抽象類別中的模版方法正是通過實作類別擴充的方法來完成商務邏輯。只要實作類別中的擴充方法通過了單元測試,在模版方法正確的前提下,整體功能一般不會出現大的錯誤。
模板方法模式的優點:
1. 封裝不變部分,擴充可變部分
2. 提取公用部分代碼,便於維護
3. 行為由父類控制,子類實現
模板方法模式的適用情境:
1. 多個子類有公用方法,並且邏輯基本相同
2. 對複雜的演算法,核心演算法設計為模板方法,細節功能則由各個子類實現
3. 重構代碼
模板方法模式的擴充
- 基本方法由於不需要對外提供訪問,因此可以設計為protected類型
- 設計鉤子方法(Hook Method):對外部提供介面,可以影響模板方法內的具體執行順序
總結:
父類建立架構,子類在重寫了父類部分方法後,再調用從父類繼承的方法,產生不同的結果。