1. 程式人生 > >模板方法模式(Template Method)及應用

模板方法模式(Template Method)及應用

rri style ear result lists prepare wap over ons

模板方法模式定義了一個算法的步驟,並允許子類別為一個或多個步驟提供其實踐方式。讓子類別在不改變算法架構的情況下,重新定義算法中的某些步驟。(來自百度百科)

  模板方法模式在框架中經常使用,學習此模式後,對於閱讀源碼能力會有很大的提升。我準備先描述生活中的實際場景,引申出模板方式模式,然後分析此模式在JDK中的使用,最後分析在框架SpringMVC中的使用。

1、沖咖啡和泡奶茶如何抽取成一個模板

1.1 模板思路

沖咖啡步驟

(1)把水煮沸

(2)把沸水沖咖啡

(3)倒進杯子裏

(4)加奶加糖

泡奶茶步驟

(1)把水煮沸

(2)把沸水泡茶葉

(3)倒進杯子裏

(4)加點奶

  可以發現,沖咖啡和泡奶茶有兩個步驟是一樣的:把水煮沸和倒入杯子中,有兩個步驟不一樣。那麽是不是將兩個相同的方法抽取在父類當中,用final修飾,讓咖啡和奶茶兩個子類去繼承這個父類,兩個子類就具有了父類把水煮沸和倒入杯子中的能力。再去分別實現另外兩個步驟就可以了,最後完成沖咖啡和泡奶茶的流程。

  可是,這種做法對於父類來說,整個步驟是不可控的,雖然子類具有了父類的方法,但是采用了怎樣的步驟順序去制作咖啡和奶茶,父類完全不清楚。所以,這裏要做出一些改進,如果我們能在父類中將咖啡或者奶茶的每一個步驟都定義好,需要子類實現的方法定義成抽象類,然後將整個流程封裝在一個方法裏,子類繼承父類後直接調用父類的這個方法,就能完全控制住整個操作步驟了。

1.2 具體的代碼實現

/**
 * 茶和咖啡的抽象父類
 */
public abstract class CaffeineBeverage {
    public final  void prepareRecipe(){
        boilWater();
        brew();
        pourCup();
        addCondiment();
    }

    
abstract void brew(); abstract void addCondiment(); void boilWater(){ System.out.println("燒水"); } final void pourCup(){ System.out.println("倒入杯子中"); } }
public class Coffee extends CaffeineBeverage {
    @Override
    void brew() {
        System.out.println(
"加入咖啡"); } @Override void addCondiment() { System.out.println("加入奶泡和糖"); } }
public class Tea extends CaffeineBeverage {
    @Override
    void brew() {
        System.out.println("放入茶葉");
    }

    @Override
    void addCondiment() {
        System.out.println("加點奶");
    }
}
public class TemplateMethodPattern {
    public static void main(String[] args) {
        Coffee coffee = new Coffee();
        coffee.prepareRecipe();
    }
}

1.3 代碼分析

  使用了模板方法,子類只需要去繼承父類並實現父類中的抽象方法,但具體的執行步驟還是在父類CaffeineBeverage中定義並用final修飾的,這一點保證了步驟的統一。另外,在父類中具體的方法並不一定要被final修飾,可以由子類去決定用不用或怎麽使用。重點的是模板方法的思想,父類控制流程,子類控制某些步驟。

  另外,還有一點,創建模板對象時,要不要用模板對象去接收?這個問題我的想法是可以,看具體的使用場景。

2、 Java API中的模板方法思想

2.1 數組的排序

平時我們對操作數據排序時,會使用Arrays.sort(Object[] o)方法。sort()調用的最重要的方法是mergeSort(),他就是一個模板方法,依賴實現類實現comparaTo()完成排序。sort()方法和mergeSort()方法這些都是靜態方法之間的調用,在比較數據中的對象時,會先將對象強轉成Comparable,調用CompareTo()進行比較,如果需要交換位置,則調用swap()。這也就是為什麽,當我們自定義類排序時,一定要實現Compareble並重寫compareTo()。

public class Arrays {
...
    public static void sort(Object[] a) {
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a);
        else
            ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
    }
    private static void legacyMergeSort(Object[] a) {
        Object[] aux = a.clone();
        mergeSort(aux, a, 0, a.length, 0);  
    }

  //排序中的模板方法,依賴comparaTo()方法的實現完成
private static void mergeSort(Object[] src, Object[] dest, int low, int high, int off) { int length = high - low; // Insertion sort on smallest arrays if (length < INSERTIONSORT_THRESHOLD) { for (int i=low; i<high; i++) for (int j=i; j>low && ((Comparable) dest[j-1]).compareTo(dest[j])>0; j--) swap(dest, j, j-1); return; } // Recursively sort halves of dest into src int destLow = low; int destHigh = high; low += off; high += off; int mid = (low + high) >>> 1; mergeSort(dest, src, low, mid, -off); mergeSort(dest, src, mid, high, -off); // If list is already sorted, just copy from src to dest. This is an // optimization that results in faster sorts for nearly ordered lists. if (((Comparable)src[mid-1]).compareTo(src[mid]) <= 0) { System.arraycopy(src, low, dest, destLow, length); return; } // Merge sorted halves (now in src) into dest for(int i = destLow, p = low, q = mid; i < destHigh; i++) { if (q >= high || p < mid && ((Comparable)src[p]).compareTo(src[q])<=0) dest[i] = src[p++]; else dest[i] = src[q++]; } } ... }

2.2 代碼分析

  看到這裏,可能會有些迷惑,這裏為什麽沒有用繼承,模板方法的核心不應該是繼承嗎?其實並不是這樣,只要能讓流程在類中控制住,子類或實現類實現需要實現的部分,這就是一個完整的模板方法模式。sort方法是靜態方法,就可以在所有數組中使用,只需要這個數組中的對象實現Comparable,這樣用起來更加方便,假如是繼承的話,是沒法保證每一個類都能繼承Comparable,畢竟Java只支持單繼承。

3、SpringMVC中的模板方法

未完待續...

  

模板方法模式(Template Method)及應用