模板方法模式

先上類圖:


模式觀念

模板方法模式在一個方法中定義一個算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以在不改變算法結構的情況下,重新定義算法中的某些步驟。
該設計模式主要針對這樣一種場景:當要做一件事兒的時候,這件事兒的步驟是固定好的,但是每一個步驟的具體實現方式是不一定的。這樣,我們可以把所有要做的事兒抽象到一個抽象類中,并在該類中定義一個模板方法。

簡單舉例

去銀行的營業廳辦理業務需要以下步驟:1.取號、2.辦業務、3.評價。三個步驟中取號和評價都是固定的流程,每個人要做的事兒都是一樣的。但是辦業務這個步驟根據每個人要辦的事情不同所以需要有不同的實現。我們可以將整個辦業務這件事兒封裝成一個抽象類:

public abstract class AbstractBusinessHandeler {
    /**
     * 模板方法
     */
    public final void execute(){
        getRowNumber();
        handle();
        judge();
    }
    /**
     * 取號
     * @return
     */
    private void getRowNumber(){
        System.out.println("rowNumber-00" + RandomUtils.nextInt());
    }
    /**
     * 辦理業務
     */
    public abstract void handle(); //抽象的辦理業務方法,由子類實現
    /**
     * 評價
     */
    private void judge(){
        System.out.println("give a praised");
    }
}

類中定義了四個方法,其中getRowNumber、judge這兩個方法是私有的非抽象方法。他們實現了取號和評價的業務邏輯,因為這兩部分內容是通用的。還有一個抽象的handle方法,這個方法需要子類去重寫,根據辦理業務的具體內容重寫該方法。還有一個模板方法就是final類型的execute方法,他定義好了需要做的事兒和做這些事兒的順序。

現在,有了這個抽象類和方法,如果有人想要辦理業務,那么只需要繼承該AbstractBusinessHandeler并且重寫handle方法,然后再使用該實現類的對象調用execute方法,即可完成整個辦理業務的流程。

public class SaveMoneyHandler extends AbstractBusinessHandeler {
    @Override
    public void handle() {
        System.out.println("save 1000");
    }
    public static void main(String []args){
        SaveMoneyHandler saveMoneyHandler = new SaveMoneyHandler();
        saveMoneyHandler.execute();
    }
}//output:編號:rowNumber-001   save 1000   give a praised

鉤子(do not call me, i will call you的經典體現)

在抽象模板里提供一個鉤子,子類可根據情況選擇實現方式,或者不去實現,鉤子使得子類有決定父類型為的能力。加入上面的銀行業務例子中加入會員功能(會員不用排隊),改寫抽象模板如下:

public abstract class AbstractBusinessHandeler {

    public final void execute(){ //定義為final,禁止子類覆蓋該方法
        if(!isVip()){//如果顧客是vip,則不用排隊
            getRowNumber();
        }
        handle();
        judge();
    }

    //抽象的鉤子方法,由子類實現    
    public boolean isVip(){
        return false; //父類提供默認實現
     }

    private void getRowNumber(){
        System.out.println("rowNumber-00" + RandomUtils.nextInt());
    }

    public abstract void handle(); 

    private void judge(){
        System.out.println("give a praised");
    }
}

荒野中模板(一些一眼認不出來的隱晦模板方法)

jdk中的arrays提供的對object進行sort的方法,源碼如下:

/**
     * Sorts the specified array of objects into ascending order, according
     * to the {@linkplain Comparable natural ordering} of its elements.
     * All elements in the array must implement the {@link Comparable}
     * interface.  Furthermore, all elements in the array must be
     * <i>mutually comparable</i> (that is, {@code e1.compareTo(e2)} must
     * not throw a {@code ClassCastException} for any elements {@code e1}
     * and {@code e2} in the array).
     *
     * <p>This sort is guaranteed to be <i>stable</i>:  equal elements will
     * not be reordered as a result of the sort.
     *
     * <p>Implementation note: This implementation is a stable, adaptive,
     * iterative mergesort that requires far fewer than n lg(n) comparisons
     * when the input array is partially sorted, while offering the
     * performance of a traditional mergesort when the input array is
     * randomly ordered.  If the input array is nearly sorted, the
     * implementation requires approximately n comparisons.  Temporary
     * storage requirements vary from a small constant for nearly sorted
     * input arrays to n/2 object references for randomly ordered input
     * arrays.
     *
     * <p>The implementation takes equal advantage of ascending and
     * descending order in its input array, and can take advantage of
     * ascending and descending order in different parts of the the same
     * input array.  It is well-suited to merging two or more sorted arrays:
     * simply concatenate the arrays and sort the resulting array.
     *
     * <p>The implementation was adapted from Tim Peters's list sort for Python
     * (<a >
     * TimSort</a>).  It uses techniques from Peter McIlroy's "Optimistic
     * Sorting and Information Theoretic Complexity", in Proceedings of the
     * Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474,
     * January 1993.
     *
     * @param a the array to be sorted
     * @throws ClassCastException if the array contains elements that are not
     *         <i>mutually comparable</i> (for example, strings and integers)
     * @throws IllegalArgumentException (optional) if the natural
     *         ordering of the array elements is found to violate the
     *         {@link Comparable} contract
     */
    public static void sort(Object[] a) {
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a);
        else
            ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
    }

具體實現中部分代碼如下:

@SuppressWarnings({"fallthrough", "rawtypes", "unchecked"})
    private static void binarySort(Object[] a, int lo, int hi, int start) {
        assert lo <= start && start <= hi;
        if (start == lo)
            start++;
        for ( ; start < hi; start++) {
            Comparable pivot = (Comparable) a[start]; //!!?。。。“裲bject轉換為Comparable

            // Set left (and right) to the index where a[start] (pivot) belongs
            int left = lo;
            int right = start;
            assert left <= right;
            /*
             * Invariants:
             *   pivot >= all in [lo, left).
             *   pivot <  all in [right, start).
             */
            while (left < right) {
                int mid = (left + right) >>> 1;
                if (pivot.compareTo(a[mid]) < 0)
                    right = mid;
                else
                    left = mid + 1;
            }
            assert left == right;

            /*
             * The invariants still hold: pivot >= all in [lo, left) and
             * pivot < all in [left, start), so pivot belongs at left.  Note
             * that if there are elements equal to pivot, left points to the
             * first slot after them -- that's why this sort is stable.
             * Slide elements over to make room for pivot.
             */
            int n = start - left;  // The number of elements to move
            // Switch is just an optimization for arraycopy in default case
            switch (n) {
                case 2:  a[left + 2] = a[left + 1];
                case 1:  a[left + 1] = a[left];
                         break;
                default: System.arraycopy(a, left, a, left + 1, n);
            }
            a[left] = pivot;
        }
    }

最關鍵的部分就是Comparable pivot = (Comparable) a[start];

待比較的對象必須實現Comparable接口,這里的實現方式并不是傳統的繼承實現。

  • 看起來更像是策略模式?

上面的代碼確實是使用了組合的方式實現,但是策略模式被組合進來的類是被委托去實現整個算法內容的,而數組的排序這里排序不走基本都已經實現完了,就差具體比較邏輯了,所以更像是模板方法模式,很多模板方法模式估計比較難分辨出來,蛋蛋的憂傷。。。。

和工廠方法模式的區別?

實現方式簡直一模一樣,只是出發點不同,工廠方法是為了按需創建具體對象,模板方法完全是為了封裝變化的算法,而且通常會有大于一個的抽象方法。

和策略模式的區別?

1.策略模式只對某個算法的整體進行封裝,模板方法會去控制算法的步驟流程。(這一點其實不算是什么區別。。。)
2.模板方法在代碼復用性,預留鉤子等方面非常出色(因為抽象父類的存在)
3.策略模式是使用組合實現的,模板方法是繼承(策略模式相當靈活,運行時也可動態替換算法。)

----自己使用的最多的模式之一,謝謝模板方法模式~~~致敬

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容