模板方法模式(Template Method Pattern)——複雜流程步驟的設計

模式概述

在現實生活中,很多事情都包含幾個實現步驟,例如請客喫飯,無論喫什麼,一般都包含點單、喫東西、買單等幾個步驟,通常情況下這幾個步驟的次序是:點單 --> 喫東西 --> 買單。

在這三個步驟中,點單和買單大同小異,最大的區別在於第二步——喫什麼?不同的人、不同的地方有不同的飲食習慣。如果站在軟件工程的角度來看,這就是變化的地方,也可以說是可擴展的點。

軟件開發中,經常會遇到類似的情況,某個方法的實現需要多個步驟(類似“請客”),其中有些步驟是固定的(類似“點單”和“買單”),而有些步驟並不固定,存在可變性(類似“喫東西”)。

爲了提高代碼的複用性和系統的靈活性,可以使用一種稱之爲模板方法模式的設計模式來對這類情況進行設計。

在模板方法模式中,將實現功能的每一個步驟所對應的方法稱爲基本方法(例如“點單”、“喫東西”和“買單”),而調用這些基本方法同時定義基本方法的執行次序的方法稱爲模板方法(例如“請客”)。

在模板方法模式中,可以將相同的代碼放在父類中,例如將模板方法“請客”以及基本方法“點單”和“買單”的實現放在父類中,而對於基本方法“喫東西”,在父類中只做一個聲明,將其具體實現放在不同的子類中,在一個子類中提供“吃麪條”的實現,而另一個子類提供“喫米飯”的實現。

通過使用模板方法模式,一方面提高了代碼的複用性,另一方面還可以利用面向對象的多態性,在運行時選擇一種具體子類,實現完整的“請客”方法,提高系統的靈活性和可擴展性。

模式定義

模板方法模式定義如下:

模板方法模式(Template Method Pattern):定義一個操作中算法的框架,而將一些步驟延遲到子類中。模板方法模式使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。

模板方法模式是一種基於繼承的代碼複用技術,它是一種類行爲型模式。

模板方法模式是結構最簡單的行爲型設計模式,在其結構中只存在父類與子類之間的繼承關係。通過使用模板方法模式,可以將一些複雜流程的實現步驟封裝在一系列基本方法中,在抽象父類中提供一個稱之爲模板方法的方法來定義這些基本方法的執行次序,而通過其子類來覆蓋某些步驟,從而使得相同的算法框架可以有不同的執行結果。

模板方法模式提供了一個模板方法來定義算法框架,而某些具體步驟的實現可以在其子類中完成。

模式結構圖

模板方法模式結構比較簡單,其核心是抽象類和其中的模板方法的設計,其結構如下圖所示:

模板方法模式包含如下兩個角色:

  • AbstractClass(抽象類):在抽象類中定義了一系列基本操作(Primitive Operations),這些基本操作可以是具體的,也可以是抽象的,每一個基本操作對應算法的一個步驟,在其子類中可以重定義或實現這些步驟。同時,在抽象類中實現了一個模板方法(Template Method),用於定義一個算法的框架,模板方法不僅可以調用在抽象類中實現的基本方法,也可以調用在抽象類的子類中實現的基本方法,還可以調用其他對象中的方法。
  • ConcreteClass(具體子類):它是抽象類的子類,用於實現在父類中聲明的抽象基本操作以完成子類特定算法的步驟,也可以覆蓋在父類中已經實現的具體基本操作。

模式僞代碼

抽象父類AbstractClass負責給出一個算法的輪廓和框架,如下:

/**
 * 抽象模板
 */
public abstract class AbstractClass {

    /**
     * 具體方法1
     */
    public void method1() {

    }

    /**
     * 抽象方法2
     */
    public abstract void method2();

    /**
     * 具體方法3
     */
    public void method3() {
        
    }

    /**
     * 模板方法
     */
    public void templateMethod() {
        method1();
        method2();
        method3();
    }
}

其中實現這些具體邏輯步驟的方法即爲基本方法,而將這些基本方法彙總起來的方法即爲模板方法

子類ConcreteClass負責給出這個算法的各個邏輯實現,如下:

public class ConcreteClass extends AbstractClass {

    @Override
    public void method2() {
        // 抽象步驟的實現
    }

    @Override
    public void method3() {
        // 也可覆蓋父類中已經實現的具體方法
        super.method3();
    }
}

這樣,就可以通過擴展不同的子類來覆蓋某些步驟,從而使得相同的算法框架可以有不同的執行結果。

模式總結

寫代碼的一個很重要的思考點就是變與不變,程序中哪些功能是可變的,哪些功能是不變的,我們可以把不變的部分提取出來,進行公共的實現,把變化的部分分離出來,用接口來封裝隔離變化,或用抽象類約束子類行爲。模板方法模式就很好的體現了這一點。

模板方法模式是基於繼承的代碼複用技術,它體現了面向對象的諸多重要思想,是一種使用較爲頻繁的模式。模板方法模式廣泛應用於框架設計中,以確保通過父類來控制處理流程的邏輯順序(如框架的初始化,測試流程的設置等)。

主要優點

  • 在父類中形式化地定義一個算法,而由它的子類來實現細節的處理,在子類實現詳細的處理算法時並不會改變算法中步驟的執行次序
  • 模板方法模式是一種代碼複用技術,它提取了公共行爲,將公共行爲放在父類中,而通過其子類來實現不同的行爲,它鼓勵我們恰當使用繼承來實現代碼複用
  • 可實現一種反向控制結構,通過子類覆蓋父類的方法來決定某一特定步驟是否需要執行
  • 通過子類來覆蓋父類的基本方法,不同的子類可以提供基本方法的不同實現,更換和增加新的子類很方便,符合單一職責原則和開閉原則

主要缺點

需要爲每一個基本方法的不同實現提供一個子類,如果父類中可變的基本方法太多,將會導致類的個數增加,系統更加龐大,設計也更加抽象,此時,可結合 橋接模式 來進行設計。

適用場景

  • 對一些複雜的算法進行分割,將其算法中固定不變的部分設計爲模板方法和父類具體方法,而一些可以改變的細節由其子類來實現
  • 各子類中公共的行爲應被提取出來並集中到一個公共父類中
  • 需要通過子類來決定父類算法中某個步驟是否執行,實現子類對父類的反向控制

參考書籍:

《設計模式的藝術之道(軟件開發人員內功修煉之道)》—— 劉偉

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章