簡單記錄 設計模式之禅-秦小波 & 軟體秘笈-設計模式那些事-鄭阿奇
文章目錄
- 1、模闆方法模式的定義
- 2、模闆方法的實作
-
- 模闆方法的通用類圖
- 模闆方法中的角色
- 模闆方法通用代碼
- 3、模闆方法模式的應用
-
- 模闆方法模式的優點
- 模闆方法模式的缺點
- 模版方法的使用場景
- 4、設計原則
-
- 1.“開-閉”原則
- 2.好萊塢原則
- 5、模闆方法模式的使用場合
- 6、模闆方法模式的擴充
- 7、最佳實踐
- 8、模闆方法總結
-
- 模闆方法模式(Template Method Pattern)
- 模闆方法模式的實作要素
- 設計原則
- 模式中的角色
- 相關的設計模式
- 使用場合
- 模闆方法模式的應用
-
- 1.模闆方法模式的優點
- 2.模闆方法模式的缺點
- 模闆方法模式的使用場景
1、模闆方法模式的定義
模闆方法模式(Template Method Pattern)其定義如下:
Define the skeleton of an algorithm in anoperation,deferring some steps to subclasses.Template Method lets subclasses redefine certain steps of analgorithm without changing the algorithm’s structure.
(定義一個操作中的算法的架構,而将一些步驟延遲到子類中。使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。)
模闆方法模式是比較簡單的一種設計模式,但是它卻是代碼複用的一項基本的技術,在類庫中尤其重要,它遵循"抽象類應當擁有盡可能多的行為,應當擁有盡可能少的資料”的重構原則。作為模闆的方法要定義在父類中,在方法的定義中使用抽象方法,而隻看父類的抽象方法是根本不知道怎樣處理的,實際具體處理的是子類,在子類中實作具體功能,是以不同的子類執行将會得出不同的實作結果,但是處理流程還是按照父類定制的方式。這就是模闆方法的要義所在,制定算法骨架,讓子類具體實作。
模闆方法的要義所在,制定算法骨架,讓子類具體實作。
2、模闆方法的實作
模闆方法的通用類圖
模闆方法模式的通用類圖如圖所示。
模闆方法模式是非常簡單的,僅僅使用了Java的繼承機制,但它是一個應用非常廣泛的模式。
其中,AbstractClass叫做抽象模闆,它的方法分為兩類:
●基本方法,基本方法也叫做基本操作,是由子類實作的方法,并且在模闆方法被調用。
●模闆方法可以有一個或幾個,一般是一個具體方法,也就是一個架構,實作對基本方法的排程,完成固定的邏輯。
注意 為了防止惡意的操作,一般模闆方法都加上final關鍵字,不允許被覆寫。
在類圖中還有一個角色:具體模闆。
ConcreteClass1和ConcreteClass2屬于具體模闆,實作父類所定義的一個或多個抽象方法,也就是父類定義的基本方法在子類中得以實作。
模闆方法中的角色
模闆方法模式中的角色
(1)抽象參與者(AbstractClass):在抽象參與者中,聲明了模闆方法,在模闆方法中實作業務處理邏輯。其中尚未實作的抽象方法由子類實作。
(2)具體參與者(ConcreteClass):具體參與者實作抽象參與者中定義的抽象方法,進而實作整個模闆方法處理流程。
模闆方法通用代碼
模闆方法通用代碼,
AbstractClass如代碼所示。
代碼 抽象模闆類
public abstract class AbstractClass {
//基本方法
protected abstract void doSomething();
//基本方法
protected abstract void doAnything();
//模版方法
public void templateMethod(){
/*
* 調用基本方法,完成相關的邏輯
*/
this.doAnything();
this.doSomething();
}
}
具體模闆 如代碼所示 ConcreteClass
public class ConcreteClass1 extends AbstractClass {
//實作基本方法
protected void doAnything() {
//業務邏輯處理
}
protected void doSomething() {
//業務邏輯處理
}
}
具體模闆類
public class ConcreteClass2 extends AbstractClass {
//實作基本方法
protected void doAnything() {
//業務邏輯處理
}
protected void doSomething() {
//業務邏輯處理
}
}
場景類如代碼所示。代碼 場景類
public class Client {
public static void main(String[] args) {
AbstractClass class1 = new ConcreteClass1();
AbstractClass class2 = new ConcreteClass2();
//調用模版方法
class1.templateMethod();
class2.templateMethod();
}
}
private、protected、public
基本方法 思考一下,可用替換為public修飾麼?
可以用public,public和private的主要目的是讓子類可以通路要修改的方法,但protected比public有更好的封裝性,用protected更好點。這些抽象方法通常隻是讓子類看到并實作,通常沒必要暴露給外部。
注意 抽象模闆中的基本方法盡量設計為protected類型,符合迪米特法則,不需要暴露的屬性或方法盡量不要設定為protected類型。實作類若非必要,盡量不要擴大父類中的通路權限。
3、模闆方法模式的應用
模闆方法模式的優點
優點
●封裝不變部分,擴充可變部分。
把認為是不變部分的算法封裝到父類實作,而可變部分的則可以通過繼承來繼續擴充。
擴充,增加一個子類,實作父類的基本方法就行了。
●提取公共部分代碼,便于維護。
如果不抽取公共部分代碼到父類中,任由這種散亂的代碼發生,想想後果是什麼樣子?維護人員為了修正一個缺陷,需要到處查找類似的代碼!
●行為由父類控制.子類實作基本方法。
基本方法是由子類實作的,是以子類可以通過擴充的方式增加相應的功能,符合開閉原則。
模闆方法模式的缺點
按照我們的設計習慣,抽象類負責聲明最抽象、最一般的事物屬性和方法,實作類完成具體的事物屬性和方法。但是模闆方法模式卻颠倒了,抽象類定義了部分抽象方法,由子類實作,子類執行的結果影響了父類的結果,也就是子類對父類産生了影響,這在複雜的項目中,會帶來代碼閱讀的難度,而且也會讓新手産生不适感。
模版方法的使用場景
多個子類有公有的方法,并且邏輯基本相同時。重要、複雜的算法,可以把核心算法設計為模闆方法,周邊的相關細節功能則由各個子類實作。重構時,模闆方法模式是一個經常使用的模式,把相同的代碼抽取到父類中,然後通過鈎子函數限制其行為。
4、設計原則
1.“開-閉”原則
“開-閉”原則是指一個軟體實體應該對擴充開放,對修改關閉,也就是說軟體實體應該是在不被修改的情況下被擴充。
模闆方法模式的意圖是由抽象父類控制頂級邏輯,并把某些基本操作的實作推遲到子類去實作,這是通過繼承的手段來達到對象複用的目的,不同的子類實作不同的具體功能,遵守了“開-閉”原則。
在模闆方法模式中,在父類中定義一個頂級的骨架邏輯,并提供一個具體的實作方法供外部調用,稱為模闆方法。
這裡有兩點内容需要注意,
一是模闆方法不能被子類修改,必須使用父類提供的骨架算法,是以需要将模闆方法定義為final的方法(在Java中為不可重寫的方法),進而保證父類控制算法邏輯;
二是由子類實作的抽象方法需要定義為protected abstract,使資料不被外部對象惡意通路及錯誤使用而破壞封裝性。
2.好萊塢原則
“不要打電話給我們,我們會打電話給你”——這是著名的好萊塢原則。
在好萊塢,把履歷遞交給演藝公司後就隻有回家等待消息,整個過程是由演藝公司完全控制的,作為演員隻能是被動式地接受。
模闆方法模式充分地展現了好萊塢原則。由父類完全控制着子類的處理邏輯,子類可以實作父類的可變部分,但是卻繼承父類的邏輯,不能改變業務邏輯。其實這就是所謂的控制反轉(IOC)。
好萊塢原則是一個有效防止“依賴腐敗”的方法,所謂“依賴腐敗”是指高層元件和低層元件之間互相依賴,高層元件又與其他邊側元件存在依賴關系,邊側元件與低層元件又有依賴關系,錯綜複雜的依賴關系讓人理不清頭緒。
好萊塢原則允許低層元件将自己挂鈎到高層元件的算法過程中,什麼時候調用,則是按照高層的處理邏輯決定,有效避免了系統環狀依賴。
5、模闆方法模式的使用場合
使用場合
(1)一次性實作一個算法不變的部分,并将可變的行為留給子類來實作;
(2)各子類中具有公共行為的時候,應被提取出來并集中到一個公共父類中以避免代碼重複;
(3)當需要控制子類擴充的時候。模闆方法在特定點調用鈎子操作,這樣就隻允許在這些點進行擴充。
鈎子???
所謂鈎子函數是什麼?
指向子類對象的引用,由子類複寫差異化,說的好聽點
鈎子使子類更靈活
鈎子方法
Hook,鈎子函數,提供一個預設或空的實作
具體的子類可以自行決定是否挂鈎以及如何挂鈎
鈎住 複寫 protected
挂鈎,更大的靈活性 選擇性。
“鈎子”的含義,所謂“鈎子”就是聲明在抽象基類中的方法,但是該方法是空的或者是含預設的實作。在子類中,可以根據需要重載“鈎子”,進而讓子類有能力對抽象基類中骨架算法的不同點進行挂鈎。重載抽象基類中的方法,進而實作子類的特有行為。
在設計一個軟體系統的時候,通常會遇到這樣一個問題:我們知道一個算法所需要的關鍵步驟,并确定了這些步驟的執行順序,但是某些步驟的具體實作是未知的,或者說某些步驟的實作與具體的環境相關。這個時候,模闆方法模式就有用武之地了。模闆方法模式把我們不知道具體實作的步驟封裝成抽象方法,提供一個按正确順序調用它們的具體方法,這些具體方法稱為“模闆方法”,提供給外部調用,這樣構成一個抽象基類。子類通過繼承這個抽象基類去實作各個步驟的抽象方法,而流程處理邏輯卻是由父類來控制的。
在應用模闆方法模式的時候,首先需要定義一個抽象的基類,在這個抽象類中,需要包含一個模闆方法,即不能被子類修改的public final方法。在模闆方法中,實作業務處理邏輯。模闆方法中還包括一些本類中的鈎子方法,用于讓子類重載實作不同的處理邏輯。根據業務的需要,定義各種不同的子類,實作模闆方法類的所有抽象方法,這樣就形成了模闆方法模式。
6、模闆方法模式的擴充
在抽象類中,影響了模闆方法的執行結果,該方法就叫做鈎子方法(Hook Method)。有了鈎子方法模闆方法模式才算完美,可以想想,由子類的一個方法傳回值決定公共部分的執行結果,是不是很有吸引力呀!模闆方法模式就是在模闆方法中按照一定的規則和順序調用基本方法。
擴充1:Java SDK中的模闆方法模式模闆方法模式是比較簡單的一種設計模式,但是它卻是代碼複用的一項基本的技術,在類庫中尤其重要。在抽象父類中定義算法骨架,子類實作具體功能,處理流程還是按照父類定制的方式。例如,JDK中java.util.Arrays數組中的sort排序算法就是典型的模闆方法模式。
擴充2:相關的設計模式工廠方法模式:工廠方法模式是模闆方法模式的特殊情況,工廠方法模式中使用子類來産生對象執行個體。政策模式:政策模式中使用委托關系切換整個算法,不是部分修改算法内容,整個算法結構已經改變了。而模闆方法模式則是使用繼承的方式,讓子類實作部分的算法内容,進而實作整個算法處理,但是算法骨架仍然是由抽象基類決定的。
7、最佳實踐
父類建立架構,子類在重寫了父類部分的方法後,再調用從父類繼承的方法,産生不同的結果(而這正是模闆方法模式)。這是不是也可以了解為父類調用了子類的方法呢?你修改了子類,影響了父類行為的結果,曲線救國的方式實作了父類依賴子類的場景,模闆方法模式就是這種效果。
模闆方法在一些開源架構中應用非常多,它提供了一個抽象類,然後開源架構寫了一堆子類。如果你需要擴充功能,可以繼承這個抽象類,然後覆寫protected方法,再然後就是調用一個類似execute方法,就完成你的擴充開發,非常容易擴充的一種模式。
8、模闆方法總結
模闆方法模式(Template Method Pattern)
模闆方法定義(Template Method Pattern),定義一個操作中的算法骨架,而将一些實作步驟延遲到子類當中。模闆方法使得子類可以在不改變算法結構的情況下,重新定義算法中的某些步驟。
模闆方法模式的通用類圖如下圖:
模闆方法模式确實非常簡單,僅僅使用了Java的繼承機制,但是它是一個應用非常廣泛的模式。其中,AbstractClass叫做抽象模闆,它的方法分為兩類:
- 基本方法。基本方法也叫做基本操作,是由子類實作的方法,并且在模闆方法被調用。
- 模闆方法。可以有一個或幾個,一般是一個具體方法,也就是一個骨架,實作對基本方法的排程,完成固定的邏輯。
在類圖中還有一個角色:具體模闆,ConcreteClass1和ConcreteClass2屬于具體模闆,實作父類所定義的一個或多個抽象方法,也就是父類定義的基本方法在子類中得以實作。
注意:為了防止惡意的操作,一般模闆方法都加上final關鍵字,不允許被覆寫。
模闆方法模式的實作要素
抽象基類
具體子類
抽象基類
- 基本方法 相同的 直接定義在抽象基類
- 抽象方法 不知道具體實作原則
- 可選的鈎子 提供一個預設或空的實作 具體的子類可以自行決定是否挂鈎以及如何挂鈎
-
Template方法(final)
模闆方法 封裝了所有子類共同遵循的算法架構
不能被子類複寫 可以替換
具體子類
- 實作基類中的抽象方法
- 覆寫鈎子方法
準備一個抽象類,将部分邏輯以具體方法的形式實作,然後聲明一些抽象方法交由子類實作剩餘邏輯,用鈎子方法給予子類更大的靈活性。最後将方法彙總構成一個不可改變的模版方法。
設計原則
(1)“開-閉”原則:模闆方法模式使用抽象父類控制頂級邏輯,把某些基本操作的實作推遲到子類去實作,這是通過繼承的手段來達到對象複用的目的,不同的子類實作不同的具體功能,遵守了“開-閉”原則。
(2)好萊塢原則:允許低層元件将自己挂鈎到高層元件的算法過程中,什麼時候去調用,則按照高層的處理邏輯決定。有效避免了系統環狀依賴。
模式中的角色
(1)抽象參與者(AbstractClass):在抽象參與者中,聲明了模闆方法,在模闆方法中實作業務處理邏輯。其中尚未實作的抽象方法由子類實作。(2)具體參與者(ConcreteClass):具體參與者實作抽象參與者中定義的抽象方法,進而實作整個模闆方法處理流程。
相關的設計模式
(1)工廠方法模式:工廠方法模式是模闆方法模式的特殊情況,工廠方法模式中使用子類來産生對象執行個體。
(2)政策模式:政策模式中使用委托關系切換整個算法,不是部分修改算法内容,整個算法結構已經改變了。而模闆方法模式則是使用繼承的方式,讓子類實作部分的算法内容,進而實作整個算法處理,但是算法骨架仍然是由抽象基類決定的。
使用場合
(1)一次性實作一個算法不變的部分,并将可變的行為留給子類來實作;
(2)各子類中具有公共行為的時候,應被提取出來并集中到一個公共父類中以避免代碼重複;
(3)當需要控制子類擴充的時候。模闆方法在特定點調用鈎子操作,這樣就隻允許在這些點進行擴充。
模闆方法模式的應用
1.模闆方法模式的優點
- 封裝不變部分,擴充可變部分。把認為是不變部分的算法封裝到父類實作,而可變部分的則可以通過繼承來繼續擴充。
- 提取公共部分代碼,便于維護。
- 行為控制交由子類來實作。基本方法是由子類實作的,是以子類可以通過擴充的方式增加相應的功能,符合開閉原則。
- 封裝性好
- 複用性好
- 屏蔽細節
- 便于維護
2.模闆方法模式的缺點
按照我們設計習慣,抽象類負責聲明最抽象、最一般的事物屬性和方法,實作類完成具體的事物屬性和方法,但是模闆方法模式卻颠倒了,抽象類定義了部分抽象方法,由子類實作,子類執行的結果影響了父類的結果,也就是子類對父類産生了影響,這在複雜的項目中,會帶來代碼閱讀的難度,而且也會讓新手産生不适感。
- 繼承 Java單繼承
模闆方法模式的使用場景
- 多個子類有公有的方法,并且邏輯基本相同時。
- 重要、複雜的算法,可以把核心算法設計為模闆方法,周邊的相關細節功能則由各個子類實作。
- 重構時,模闆方法模式是一個經常使用的模式,把相同的代碼抽取到父類中,然後通過鈎子函數(見“模闆方法模式的擴充”)限制其行為。
(1)算法或操作遵循相似的邏輯
(2)重構時(把相同的代碼抽取到父類中)
共性 個性交給不同的子類去實作
(3) 重要、複雜的算法,核心算法設計為模闆算法