模板方法模式屬于行為型模式,定義一個操作中的演算法骨架,而將演算法的一些步驟延遲到子類中,使得子類可以不改變該演算法結構的情況下重定義該演算法的某些特定步驟,不同的子類可以以不同的方式實作這些抽象方法,從而對剩余邏輯有不同的實作,模版方法模式是基于繼承的代碼復用的基本技術,模版方法模式的結構和用法也是面向物件設計的核心,
模板方法模式的UML類圖如下:

從上圖可以看出,模板方法模式主要有抽象類角色、具體子類角色兩種角色:
-
抽象類角色:
定義了一個或多個抽象操作,以便讓子類實作,這些抽象操作叫做基本操作,它們是一個頂級邏輯的組成步驟,
定義并實作了一個模版方法,這個模版方法一般是一個具體方法,它給出了一個頂級邏輯的骨架,而邏輯的組成步驟在相應的抽象操作中,推遲到子類實作,頂級邏輯也有可能呼叫一些具體方法,
-
具體子類角色:
實作父類所定義的一個或多個抽象方法,它們是一個頂級邏輯的組成步驟,
每一個抽象模版角色都可以有任意多個具體模版角色與之對應,而每一個具體模版角色都可以給出這些抽象方法(也就是頂級邏輯的組成步驟〉的不同實作,從而使得頂級邏輯的實作各不相同,
銀行存款例子
一個系統需要計算存款利息,主要有兩種存款賬號,即貨幣市場賬戶(Money Market ACcount)和定期賬戶(CDAccount);這兩種賬戶的存款利率是不通的,因此,在計算一個賬戶的存款利息額的時候,必須磁區兩種不同的賬戶型別,
那么在這個系統中,主要有兩個方法,一是確定賬戶的型別,二是確定利息的百分比,在這里就可以使用模板方法在抽象類中定義兩個基本方法,然后有子類根據賬號型別的不同而給出具體的方法,
其UML類圖如下:

抽象類:
package com.charon.template;
/**
* @className: Account
* @description:
* @author: charon
* @create: 2022-03-26 14:57
*/
public abstract class Account {
private String accountNum;
/**
* 無參構造,幫助子類初始化
* 如果沒有顯示宣告父類的無參的構造方法,系統會自動默認生成一個無參構造方法,
* 但是,如果宣告了一個有參的構造方法,而沒有宣告無參的構造方法,這時系統不會動默認生成一個無參構造方法,
*/
public Account() {
}
public Account(String accountNum) {
this.accountNum = accountNum;
}
/**
* 模板方法,計算利息的數額
* 使用final修飾,可以防止子類重寫模板方法
* @return
*/
public final double calcInterest(){
double interestRate = calcInterestRate();
String accountType = calcAccountType();
double amount = getAmount(accountType,accountNum);
return interestRate * amount;
}
/**
* 獲取賬戶金額
* @param accountType 賬戶型別
* @param accountNum 賬號
* @return
*/
protected double getAmount(String accountType, String accountNum){
// 從資料庫中獲取該賬戶的金額,這里直接回傳一個
return 5000;
}
/**
* 計算賬戶型別
*/
protected abstract String calcAccountType();
/**
* 計算利息利率
* @return
*/
protected abstract double calcInterestRate();
}
具體子類:
package com.charon.template;
/**
* @className: MoneyMarketAccount
* @description:
* @author: charon
* @create: 2022-03-26 15:12
*/
public class MoneyMarketAccount extends Account{
@Override
protected String calcAccountType() {
return "Money Market";
}
@Override
protected double calcInterestRate() {
return 0.045;
}
}
package com.charon.template;
/**
* @className: CDAccount
* @description:
* @author: charon
* @create: 2022-03-26 15:22
*/
public class CDAccount extends Account {
@Override
protected String calcAccountType() {
return "CD";
}
@Override
protected double calcInterestRate() {
return 0.065;
}
}
客戶端呼叫:
package com.charon.template;
/**
* @className: Client
* @description:
* @author: charon
* @create: 2022-03-25 23:44
*/
public class Client {
public static void main(String[] args) {
Account mmAccount = new MoneyMarketAccount();
System.out.println("貨幣市場賬號的利息是:" + mmAccount.calcInterest());
Account cdAccount = new CDAccount();
System.out.println("定期賬號的利息是:" + cdAccount.calcInterest());
}
}
列印:
貨幣市場賬號的利息是:225.0
定期賬號的利息是:325.0
模板方法模式的主要優點如下:
- 它封裝了不變部分,擴展可變部分,它把認為是不變部分的演算法封裝到父類中實作,而把可變部分演算法由子類繼承實作,便于子類繼續擴展,
- 它在父類中提取了公共的部分代碼,便于代碼復用,
- 部分方法是由子類實作的,因此子類可以通過擴展方式增加相應的功能,符合“開閉原則”,
模板方法模式的主要缺點如下:
- 對每個不同的實作都需要定義一個子類,這會導致類的個數增加,系統更加龐大,設計也更加抽象,間接地增加了系統實作的復雜度,
- 父類中的抽象方法由子類實作,子類執行的結果會影響父類的結果,這導致一種反向的控制結構,它提高了代碼閱讀的難度,
- 由于繼承關系自身的缺點,如果父類添加新的抽象方法,則所有子類都要改一遍,
模式的應用場景
模板方法模式通常適用于以下場景,
- 演算法的整體步驟很固定,但其中個別部分易變時,這時候可以使用模板方法模式,將容易變的部分抽象出來,供子類實作,
- 當多個子類存在公共的行為時,可以將其提取出來并集中到一個公共父類中以避免代碼重復,首先,要識別現有代碼中的不同之處,并且將不同之處分離為新的操作,最后,用一個呼叫這些新的操作的模板方法來替換這些不同的代碼,
- 當需要控制子類的擴展時,模板方法只在特定點呼叫鉤子操作,這樣就只允許在這些點進行擴展,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/449799.html
標籤:其他
上一篇:結構性:三. 裝飾器模式
下一篇:設計模式之命令模式
