簡介
Define the skeleton of an algorithm in an operation,deferring some
steps to subclasses.Template Method lets subclasses redefine certain
steps of an algorithm without changing the algorithm’s structure.
定義一個操作中的算法的框架,而將一些步驟延遲到子類中。使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。
-----------------------------------------------------------------深入淺出的設計模式
-
模板方法模式(Template Method Pattern) 實際上是封裝了一個固定流程,該流程由幾個步驟組成,具體步驟可以由子類進行不同實現,從而讓固定的流程產生不同的結果。
-
意圖:定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。
-
模板方法模式實際上是所有模式中最爲常見的幾個模式之一,而且很多人可能使用過模板方法模式而沒有意識到自己已經使用了這個模式。模板方法模式是基於繼承的代碼複用的基本技術,模板方法模式的結構和用法也是面向對象設計的核心。模板方法的本質:抽象封裝流程,具體進行實現
-
模版方法模式需要開發抽象類和具體子類的設計師之間的協作。
- 一個設計師負責給出一個算法的輪廓和骨架
- 另一些設計師則負責給出這個算法的各個邏輯步驟。
- 代表這些具體邏輯步驟的方法稱做基本方法(primitive method)
- 將這些基本方法總彙起來的方法叫做模版方法(template method),這個設計模式的名字就是從此而來。
主要解決
當完成一個操作具有固定的流程時,由抽象固定流程步驟,具體步驟交給子類進行具體實現(固定的流程,不同的實現)。
優缺點
優點
- 封裝不變,擴展可變:父類封裝了具體流程以及實現部分不變行爲,其它可變行爲交由子類進行具體實現;
- 流程由父類控制,子類進行實現:框架流程由父類限定,子類無法更改;子類可以針對流程某些步驟進行具體實現;
- 提取公共代碼,便於維護
缺點
- 抽象規定了行爲,具體負責實現,與通常事物的行爲相反,會帶來理解上的困難(通俗地說,“父類調用了子類方法”);
- 每一個不同的實現都需要一個子類來實現,導致類的個數增加,使得系統更加龐大。
使用場景
當我們要完成在某一細節層次一致的一個過程或一系列步驟,但其個別步驟在更詳細的層次上的實現可能不同的時候,我們通常要考慮用模板方法模式解決。
- 多個子類有公有的方法,並且邏輯基本相同時;
- 重要,複雜的算法,可以把核心算法設計爲模板方法,周邊的相關細節功能則由各個子類實現;
- 重構時,模板方法模式 是一個經常使用的模式,把相同的代碼抽取到父類,然後通過鉤子函數約束其行爲;
注意事項: 爲防止惡意操作,一般模板方法都加上 final 關鍵詞。
模式講解
首先來看下 模板方法模式 的通用 UML 類圖:
模板方法模式
從 UML 類圖中,我們可以看到,模板方法模式 主要包含兩種角色:
抽象模板(AbstractClass): 抽象模板類,定義了一套算法框架/流程;
具體實現(ConcreteClass): 具體實現類,對算法框架/流程的某些步驟進行了實現;
當然這只是書面上的定義,用起來你會發現,爲什麼變得很複雜,好像更難了一樣,你也有很多疑問?
- 在抽象類中只能定義抽象函數嗎?
不是!看使用場景第三條! - 每一個抽象子類都要具體實現嗎?
語法上是的,但邏輯上,可以Do nothing - 依據第一問,我能否對非抽象方法重寫?
當然,語法上可以,而且邏輯上也可以。
最後,設計模式給出的是一種設計框架,而不是條條框框,你需要用的是他真正優秀的地方!
開發實例
就一杯下午茶:
抽象類:
abstract class AbstractClass {
protected void Boil_water() {
System.out.println("SpecificMethod:燒水,倒一杯熱水");
/*
* 模板方法的抽象類中,也是可以定義具體方法的,
* 一般子類中都具備或大部分中都具備時,放到抽象父類中。
*/
}
// 需要子類必須實現的需要寫成抽象類
protected abstract void Abstract_step1();
protected abstract void Abstract_step2();
protected void Drink() {
System.out.println("SpecificMethod:吹一吹再喝,別燙着!");
}
// 聲明爲final方法,避免子類覆寫
public final void After_Tea() {//來一杯下午茶吧
this.Boil_water();
this.Abstract_step1();
this.Abstract_step2();
this.Drink();
}
}
我要喝咖啡
class Concrete_Cof extends AbstractClass {
@Override
// 具體實現,重寫父類抽象方法
protected void Abstract_step1() {
System.out.println("把咖啡豆磨好,倒進杯子裏");
}
@Override
protected void Abstract_step2() {
System.out.println("加入方糖和奶精");
}
}
我要喝茶
class Concrete_Tea extends AbstractClass {
@Override
protected void Abstract_step1() {
System.out.println("把茶餅分開,放進杯子裏");
}
@Override
protected void Abstract_step2() {
//Do nothing
//可以寫成這種什麼都沒有的,畢竟喝茶我什麼都不加(手動狗頭)
}
}
我要喝奶茶
public class Concrete_MilkTea extends AbstractClass {
protected void Boil_water() {
System.out.println("煮一杯熱牛奶");
}
@Override
protected void Abstract_step1() {
System.out.println("把茶餅分開,放進杯子裏,過濾");
}
@Override
protected void Abstract_step2() {
System.out.println("加入方糖和香精");
}
}
class Client {
public static void main(String[] args) {
AbstractClass abc = new Concrete_Cof();
abc.After_Tea();
System.out.println("----------Next day-------------");
abc=new Concrete_Tea();
abc.After_Tea();
System.out.println("----------Next day-------------");
abc=new Concrete_MilkTea();
abc.After_Tea();
}
}
運行結果:
碼字這麼賣力,麻煩點個贊,點個關注,謝謝。