Java設計模式的模板方法模式定義一個操作中演算法的框架,而將一些步驟延遲到子類別中,使得子類別可以不改變演算法的結構即可重定義該演算法中的某些特定步驟。屬於行為類模式
如下圖所示:
事實上,模版方法是程式設計中一個常用到的模式。先來看一個例子,某日,程式設計師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
運作正常。行了,任務完成。沒錯,這就是模版方法模式。大部分剛進職場的畢業生應該都有類似B的經驗。一個複雜的任務,由公司裡的牛人們將主要的邏輯寫好,然後把那些看上去比較簡單的方法寫成抽象的,交給其他的同事去開發。這種分工方式在程式人員層級層級比較明顯的公司中常用到。例如一個專案組,有架構師,高級工程師,初級工程師,則一般由架構師使用大量的介面、抽象類別將整個系統的邏輯串起來,實現的編碼則根據難度的不同分別交給高級工程師和初級工程師來完成。怎麼樣,是不是用過模版方法模式呢?
模版方法模式的結構:
模版方法模式由一個抽象類別和一個(或一組)實作類別透過繼承結構組成,抽象類別中的方法分為三種:
1.抽象方法:父類別中只聲明但不加以實現,而是定義好規範,然後由它的子類別去實現。
2.模版方法:由抽象類別聲明並加以實作。一般來說,模版方法呼叫抽象方法來完成主要的邏輯功能,而且,模版方法大多會定義為final類型,指明主要的邏輯功能在子類別中不能被重寫。
3.鉤子方法:由抽象類別聲明並加以實作。但是子類別可以去擴展,子類別可以透過擴展鉤子方法來影響模版方法的邏輯。
抽象類別的任務是建構邏輯的框架,通常由經驗豐富的人員編寫,因為抽象類別的好壞直接決定了程式是否穩定性。
實作類別用來實作細節。抽象類別中的模版方法正是透過實作類別擴充的方法來完成業務邏輯。只要實現類別中的擴展方法通過了單元測試,在模版方法正確的前提下,整體功能一般不會出現大的錯誤。
模版方法的優點及適用場景:
容易擴展。一般來說,抽象類別中的模版方法是不易反生改變的部分,而抽象方法是容易反生變化的部分,因此透過增加實現類別一般可以很容易實現功能的擴展,符合開閉原則。
便於維護。對於模版方法模式來說,正是由於他們的主要邏輯相同,才使用了模版方法,假如不使用模版方法,任由這些相同的程式碼散亂的分佈在不同的類別中,維護起來是非常不方便的。
比較靈活。因為有鉤子方法,因此,子類別的實作也可以影響父類別中主邏輯的運作。但是,在靈活的同時,由於子類影響到了父類,違反了里氏替換原則,也會為程式帶來風險。這就對抽象類別的設計有了更高的要求。
當多個子類別擁有相同的方法,並且這些方法邏輯相同時,可以考慮使用模版方法模式。在程式的主框架相同,細節不同的場合下,也比較適合使用這種模式。