意圖
在一個方法中定義一個算法得骨架,而將一些步驟延遲到子類中。模板方法使得子類可以在不改變算法結構的情況下,重新定義算法中的某些步驟。
動機
在設計一個系統時,明確了算法所需的關鍵步驟,而且確定了這些步驟的執行順序,但某些步驟的具體實現還未知或需依賴於具體的環境。我們可以在父類中把明確的算法步驟定義爲模板方法,而未知具體實現部分延遲到子類中實現。
適用性
模板方法模式通常適用於以下場景:
- 算法的整體步驟很固定,但其中個別部分易變時,這時候可以使用模板方法模式,將容易變的部分抽象出來,在子類中實現。
- 當多個子類存在公共的行爲時,可以將其提取出來並集中到一個公共父類中以避免代碼重複。首先,要識別現有代碼中的不同之處,並且將不同之處分離爲新的操作。最後,用一個調用這些新的操作的模板方法來替換這些不同的代碼。
- 當需要控制子類的擴展時,模板方法只在特定點調用鉤子操作,這樣就只允許在這些點進行擴展。
結構
模板方法模式的主要角色有:
- 抽象類(AbstractClass):定義模板方法,給出一個算法的輪廓和骨架。它由一個模板方法和若干個基本方法構成。
- 具體子類(ConcreteClass):實現父類中所定義的抽象方法和選擇性實現鉤子方法。
實現
// 抽象類
public abstract class AbstractClass {
// 模板方法,定義了一系列的邏輯步驟,每個步驟由一個方法表示
public void templateMethod() {
hookMethod();
primitiveoperationA();
primitiveoperationB();
}
// 鉤子方法,鉤子可以讓子類實現算法中可選部分
public void hookMethod() {
System.out.println("鉤子方法,空(或默認)實現,子類重寫實現相應的邏輯。");
}
// 抽象方法,由子類實現
public abstract void primitiveoperationA();
public abstract void primitiveoperationB();
}
// 具體類
public class ConcreteClass extends AbstractClass {
@Override
public void primitiveoperationA() {
System.out.println("primitiveoperationA() 被調用。。。");
}
@Override
public void primitiveoperationB() {
System.out.println("primitiveoperationA() 被調用。。。");
}
}
// 測試客戶端
public class TestClient {
public static void main(String[] args) {
AbstractClass concrete = new ConcreteClass();
concrete.templateMethod();
}
}
從代碼實現可以看出:父類(高層組件)控制何時以及如何讓(子類方法)低層組件參與,也就是說,高層組件對低層組件的方式是“別調用我,我會調用你們”,完美的體現了好萊塢原則。
已知應用
- java.util.Collections#sort()
- java.io.InputStream#read()
相關模式
模板方法、策略模式
參考資料
- 《
Head First
設計模式》 - 圖說設計模式
- Java設計模式:23種設計模式全面解析(超級詳細)