欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java設(shè)計(jì)模式之模板方法模式Template Method Pattern詳解

 更新時(shí)間:2022年11月01日 14:56:19   作者:流煙默  
在我們實(shí)際開發(fā)中,如果一個(gè)方法極其復(fù)雜時(shí),如果我們將所有的邏輯寫在一個(gè)方法中,那維護(hù)起來就很困難,要替換某些步驟時(shí)都要重新寫,這樣代碼的擴(kuò)展性就很差,當(dāng)遇到這種情況就要考慮今天的主角——模板方法模式

概述

模板方法

模板方法定義了一個(gè)算法的步驟,并允許子類為一個(gè)或多個(gè)步驟提供實(shí)現(xiàn)。那么什么是模板方法呢?我們看下模板方法的定義。

  1. 一個(gè)具體方法而非抽象方法,其用作一個(gè)算法的模板;
  2. 在模板方法中,算法內(nèi)的大多數(shù)步驟都被某個(gè)方法代表;
  3. 模板方法中某些方法是子類處理
  4. 需要由子類提供的方法,必須在超類中聲明為抽象方法;
  5. 模板方法通常不能被覆蓋,也就是使用final修飾;

模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern),指在一個(gè)抽象類公開定義了執(zhí)行它的方法的模板。它的子類可以按需要重寫方法實(shí)現(xiàn),但調(diào)用將以抽象類中定義的方式進(jìn)行簡(jiǎn)單說,模板方法模式定義一個(gè)操作中的算法的骨架,而將一些步驟延遲到子類中,使得子類可以不改變一個(gè)算法的結(jié)構(gòu),就可以重定義該算法的某些特定步驟。

這種類型的設(shè)計(jì)模式屬于行為型模式。

如下實(shí)例,prepareRecipe就是一個(gè)模板方法。

public abstract class CaffeineBeverage {
   final void  prepareRecipe(){
       boilWater();
       brew();
       pourInCup();
       addCondiments();
   }
   protected abstract void addCondiments();
   protected abstract void pourInCup();
   protected abstract void brew();
   protected abstract void boilWater();
}

模板方法模式

在一個(gè)方法中定義一個(gè)算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以在不改變算法結(jié)構(gòu)的情況下,重新定義算法中的某些步驟。

這個(gè)模式是用來創(chuàng)建一個(gè)算法的模板。什么是模板?如你所見的,模板就是一個(gè)方法。更具體地說,這個(gè)方法將算法定義成一組步驟,其中的任何步驟都可以是抽象的,由子類負(fù)責(zé)實(shí)現(xiàn)。這可以確保算法的結(jié)構(gòu)保持不變,同時(shí)由子類提供部分實(shí)現(xiàn)。

對(duì)上面實(shí)例進(jìn)一步擴(kuò)展,我們看下抽象類內(nèi)可以有哪些類型的方法。

public abstract class CaffeineBeverage {
   final void  prepareRecipe(){
       boilWater();
       brew();
       pourInCup();
       addCondiments();
       concreteOperation();
       hook();
   }
   protected abstract void addCondiments();
   protected abstract void pourInCup();
   protected abstract void brew();
   protected abstract void boilWater();
      final void concreteOperation(){
	// 這里是實(shí)現(xiàn)
   }
	//空方法
   void hook(){};
}

可以看到有一個(gè)具體的concreteOperation方法,final表示其不可以被子類覆蓋。我們也可以由“默認(rèn)不做事的方法”,我們稱這種方法為“hook”(鉤子)。子類可以視情況決定要不要覆蓋他們。

鉤子是一種被聲明在 抽象類中的方法,但只有空的或者默認(rèn)的實(shí)現(xiàn)。鉤子的存在,可以讓子類有能力對(duì)算法的不同點(diǎn)進(jìn)行掛鉤。要不要掛鉤,由子類決定。每一個(gè)具體的子類都必須定義所有的抽象方法,并為模板方法算法中未定義步驟提供完整的實(shí)現(xiàn)。

那么什么時(shí)候使用抽象方法什么時(shí)候使用鉤子呢?

答,當(dāng)你的子類必須提供算法中某個(gè)算法或步驟的實(shí)現(xiàn)時(shí),就是用抽象方法。如果算法的這個(gè)部分是可選的,就用鉤子。如果是鉤子的話,子類可以選擇實(shí)現(xiàn)這個(gè)鉤子,但并不強(qiáng)制這么做。

使用鉤子的真正目的是什么?

鉤子有幾種用法。如我們之前所說的,鉤子可以讓子類實(shí)現(xiàn)算法中可選的部分,或者在鉤子對(duì)于子類的實(shí)現(xiàn)并不重要的時(shí)候,子類可以對(duì)此鉤子置之不理。鉤子的另一個(gè)用法,是讓子類能夠有機(jī)會(huì)對(duì)模板方法中某些即將發(fā)生的(或剛剛發(fā)生的)步驟作出反應(yīng)。比方說,名為justReOrderedList()的鉤子方法允許子類在內(nèi)部列表重新組織后執(zhí)行某些動(dòng)作(例如在屏幕上重新顯示數(shù)據(jù))。正如前面提到的,鉤子也可以讓子類有能力為其抽象類作一些決定。

好萊塢原則

好萊塢原則簡(jiǎn)單來講就是:別調(diào)用我們,我們會(huì)調(diào)用你。

好萊塢原則可以給我們一種防止“依賴腐敗”的方法。當(dāng)高層組件依賴低層組件,而低層組件又依賴高層組件,而高層組件又依賴邊側(cè)組件,邊側(cè)組件又依賴低層組件時(shí),依賴腐敗就發(fā)生了。在這種情況下,沒有人可以輕易地搞懂系統(tǒng)是如何設(shè)計(jì)的。

在好萊塢原則之下,我們?cè)试S低層組件將自己掛鉤到系統(tǒng)上,但是高層組件會(huì)決定什么時(shí)候和怎樣使用這些低層組件。換句話說,高層組件對(duì)待低層組件的方式就是“別調(diào)用我們,我們會(huì)調(diào)用你”。

模板方法模式就契合該原則。當(dāng)我們?cè)O(shè)計(jì)模板方法模式時(shí),我們告訴子類,“不要調(diào)用我們,我們會(huì)調(diào)用你”。

那么低層組件完全不可以調(diào)用高層組件的方法嗎?并不盡然!

事實(shí)上,低層組件在結(jié)束時(shí),常常調(diào)用從超類中繼承來的方法。我們所要做的是,避免讓高層和低層組件之間有明顯的環(huán)狀依賴。

好萊塢原則與依賴倒置原則

依賴倒置原則教我們盡量避免使用具體類,而多使用抽象類。而好萊塢原則是用在創(chuàng)建框架或組件上的一種技巧,好讓低層組件能夠被掛鉤進(jìn)計(jì)算中,而且又不會(huì)讓高層組件依賴低層組件。兩者的目標(biāo)都是在于解耦,但是依賴倒置原則更加注重如何在設(shè)計(jì)中避免依賴。

好萊塢原則教我們一個(gè)技巧,創(chuàng)建一個(gè)有彈性的設(shè)計(jì),允許低層結(jié)構(gòu)能夠互相操作,而又防止其他類太過依賴它們。

真實(shí)案例

數(shù)組類Arrays的mergeSort方法就是一個(gè)模板方法。

 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++];
        }
    }

這里方法中,swap方法是一個(gè)具體方法。每一個(gè)元素需要實(shí)現(xiàn)compareTo方法,“填補(bǔ)”模板方法的缺憾。

可能會(huì)有疑問,上面這個(gè)案例真的是一個(gè)模板方法嗎?

答,其符合模板方法的精神。這個(gè)模式的重點(diǎn)在于提供一個(gè)算法,并讓子類實(shí)現(xiàn)某些步驟。而數(shù)組的排序做法很明顯地并非如此!但是,我們都知道。Java api中的模式并非總是如同教科書例子一般地中規(guī)中矩,為了符合當(dāng)前的環(huán)境和實(shí)現(xiàn)的約束,它們總是要被適當(dāng)?shù)匦薷摹_@個(gè)Arrays類sort方法的設(shè)計(jì)者受到一些約束。通常我們無法設(shè)計(jì)一個(gè)類繼承Java數(shù)組,而sort()方法希望能夠適用于所有的數(shù)組(每個(gè)數(shù)組都是不同的類)。所以它們定義了一個(gè)靜態(tài)方法,而由被排序的對(duì)象內(nèi)的每個(gè)元素自行提供比較大小的算法部分。所以,這雖然不是教科書上的模板方法,但它的實(shí)現(xiàn)仍然符合模板方法模式的精神。再者,由于不需要基礎(chǔ)數(shù)組就可以使用這個(gè)方法,這樣使得排序變得更有彈性、更有用。

另外一個(gè)案例是java.io的InputStream類有一個(gè)read()方法,是由子類實(shí)現(xiàn)的,而這個(gè)方法又會(huì)被read(byte b[], int off, int len)模板方法使用。

模板方法模式的注意事項(xiàng)和細(xì)節(jié)

基本思想是:算法只存在于一個(gè)地方,也就是在父類中,容易修改。需要修改算法時(shí),只要修改父類的模板方法或者已經(jīng)實(shí)現(xiàn)的某些步驟,子類就會(huì)繼承這些修改。

實(shí)現(xiàn)了最大化代碼復(fù)用。父類的模板方法和已實(shí)現(xiàn)的某些步驟會(huì)被子類繼承而直接使用。 既統(tǒng)一了算法,也提供了很大的靈活性。父類的模板方法確保了算法的結(jié)構(gòu)保持不變,同時(shí)由子類提供部分步驟的實(shí)現(xiàn)。

該模式的不足之處:每一個(gè)不同的實(shí)現(xiàn)都需要一個(gè)子類實(shí)現(xiàn),導(dǎo)致類的個(gè)數(shù)增加,使得系統(tǒng)更加龐大。一般模板方法都加上final 關(guān)鍵字, 防止子類重寫模板方法。

模板方法模式使用場(chǎng)景:當(dāng)要完成在某個(gè)過程,該過程要執(zhí)行一系列步驟,這一系列的步驟基本相同,但其個(gè)別步驟在實(shí)現(xiàn)時(shí)可能不同,通常考慮用模板方法模式來處理。

到此這篇關(guān)于Java設(shè)計(jì)模式之模板方法模式Template Method Pattern詳解的文章就介紹到這了,更多相關(guān)Java模板方法模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論