在面向對象程序設計過程中,程序員常常會遇到這種情況:設計一個系統時知道了算法所需的關鍵步驟,而且確定了這些步驟的執行順序,但某些步驟的具體實現還未知,或者說某些步驟的實現與具體的環境相關。
例如,去銀行辦理業務一般要經過以下4個流程:取號、排隊、辦理具體業務、對銀行工作人員進行評分等,其中取號、排隊和對銀行工作人員進行評分的業務對每個客戶是一樣的,可以在父類中實現,但是辦理具體業務卻因人而異,它可能是存款、取款或者轉賬等,可以延遲到子類中實現。
這樣的例子在生活中還有很多,例如,一個人每天會起牀、吃飯、做事、睡覺等,其中“做事”的內容每天可能不同。我們把這些規定了流程或格式的實例定義成模板,允許使用者根據自己的需求去更新它,例如,簡歷模板、論文模板、Word 中模板文件等。
模板方法模式的定義
模板方法(Template Method
)模式的定義
如下:定義一個操作中的算法骨架,而將算法的一些步驟延遲到子類中,使得子類可以不改變該算法結構的情況下重定義該算法的某些特定步驟。它是一種行爲型模式
。
模板方法模式實際上是封裝了一個固定的流程,該流程由幾個步驟組成,具體步驟可以由子類進行不同的實現,從而讓固定的流程產生不同的結果。他非常簡單,其實就是類的繼承機制,但它確是一個應用廣泛的模式,模板方法模式的本質
是抽象封裝流程,具體進行實現。
模板方法模式的結構
模板方法模式包含以下主要兩個角色。
(1) 抽象模板類(Abstract Class):負責給出一個算法的輪廓和骨架。它由一個模板方法和若干個基本方法構成。這些方法的定義如下:
—① 模板方法:定義了算法的骨架,按某種順序調用其包含的基本方法。
—② 基本方法:是整個算法中的一個步驟,包含以下幾種類型:
———抽象方法:在抽象類中申明,由具體子類實現。
———具體方法:在抽象類中已經實現,在具體子類中可以繼承或重寫它。
———鉤子方法:在抽象類中已經實現,包括用於判斷的邏輯方法和需要子類重寫的空方法兩種。
(2) 具體子類(Concrete Class):實現抽象類中所定義的抽象方法和鉤子方法,它們是一個頂級邏輯的一個組成步驟。
模板方法模式的實現
/**
* 抽象模板類:疫情期間,同學們在家上網課
*/
public abstract class AbstractCourse {
//模板方法:按某種順序調用其包含的基本方法
public final void createCourse(){
//1、發佈預習資料
postPreResoucse();
//2、製作課件
createPPT();
//3、直播授課
liveVideo();
//4、上傳課後資料
postResource();
//5、佈置作業
postHomework();
//6.根據鉤子方法判斷是否需要檢查作業
if(needCheckHomework()){
checkHomework();
}
}
//抽象方法
protected abstract void checkHomework();
//鉤子方法:默認false,即不檢查作業
protected boolean needCheckHomework(){return false;}
protected void postHomework(){
System.out.println("佈置作業");
}
//以下都是具體方法
protected void postResource(){
System.out.println("上傳課後資料");
}
protected void liveVideo(){
System.out.println("直播授課");
}
protected void createPPT(){
System.out.println("製作課件");
}
protected void postPreResoucse(){
System.out.println("發佈預習資料");
}
}
/**
* 具體的子類:java課程
*/
public class JavaCourse extends AbstractCourse {
private boolean needCheckHomework = false;
public void setNeedCheckHomework(boolean needCheckHomework) {
this.needCheckHomework = needCheckHomework;
}
//重寫父類鉤子方法
@Override
protected boolean needCheckHomework() {
return this.needCheckHomework;
}
//實現檢查作業的方法
protected void checkHomework() {
System.out.println("檢查Java作業");
}
}
/**
* 具體的子類:python課程
*/
public class PythonCourse extends AbstractCourse {
protected void checkHomework() {
System.out.println("檢查Python作業");
}
}
/**
* 測試類
*/
public class Test {
public static void main(String[] args) {
System.out.println("=========架構師課程=========");
JavaCourse java = new JavaCourse();
java.setNeedCheckHomework(false);
java.createCourse();
System.out.println("=========Python課程=========");
PythonCourse python = new PythonCourse();
python.createCourse();
}
}
程序運行結果如下:java課程通過鉤子方法設置爲true,開啓檢查作業功能
=========java課程=========
發佈預習資料
製作課件
直播授課
上傳課後資料
佈置作業
檢查Java作業
=========Python課程=========
發佈預習資料
製作課件
直播授課
上傳課後資料
佈置作業
模板方法模式的應用場景
1.算法的整體步驟很固定,但其中個別部分易變時,這時候可以使用模板方法模式,將容易變的部分抽象出來,供子類實現。
2.當多個子類存在公共的行爲時,可以將其提取出來並集中到一個公共父類中以避免代碼重複。
3.當需要控制子類的擴展時,模板方法只在特定點調用鉤子操作,這樣就只允許在這些點進行擴展。
模板方法模式的優缺點
該模式的主要優點
如下:
1.它封裝了不變部分,擴展可變部分。它把認爲是不變部分的算法封裝到父類中實現,而把可變部分算法由子類繼承實現,便於子類繼續擴展。
2.它在父類中提取了公共的部分代碼,便於代碼複用。
3.部分方法是由子類實現的,因此子類可以通過擴展方式增加相應的功能,符合開閉原則。
該模式的主要缺點
如下:
1.對每個不同的實現都需要定義一個子類,這會導致類的個數增加,系統更加龐大,設計也更加抽象。
2.父類中的抽象方法由子類實現,子類執行的結果會影響父類的結果,這導致一種反向的控制結構,它提高了代碼閱讀的難度。