Java設計模式之模板方法模式
1.什麼是模板方法模式(Template Method) 爲算法的執行定義了一個統一的框架,其中的某些具體實現方法需要他在子類中去完成,每個子類所做的事情可能都不一樣,而子類可以在不改變這個框架的同時去完成該算法的某個具體步驟,這就是我們所說的模板模式。
生活中的模板:
銀行辦理業務,1、進門取號 2、填寫單據 3、等待叫號 4、窗口辦理,對於這四個規則,所有
來辦理業務的人都需要遵循這四種規則,但是在四個規則中規則2填寫單據去是需要顧客填寫的,
因爲銀行他並不知道顧客具體要辦理什麼樣的業務,所以,他只能讓顧客自己去填寫這樣的單據,
這樣銀行就是爲顧客定義了一個模板。
2.如何實現模板方法模式
1.首先需要一個抽象基類,爲所有子類提供一個算法框架
2.定義一個模板方法,封裝所有子類共同遵循的算法框架
3.將對應的步奏寫入模板方法中,將所有共同方法聲明爲私有的或者final方法,爲了隱藏實現細節
4.將所有並不知道具體實現什麼的方法設置爲抽象方法,爲了讓他的子類去實現具體細節
5.所有子類的具體實現
細節:鉤子方法實現:加入一個判斷條件,詢問用戶是否需要執行該操作,提供一個默認或空的實現,讓子類去選擇是否掛鉤,
模板方法模式的實現要素:
準備一個抽象類,將部分邏輯以具體方法的形式實現,然後聲明一些抽象方法交由子類實現剩餘邏輯,用鉤子方法給予子類更大的靈活性,最後將方法彙總構成一個不可改變的模板方法。
3.模板方法模式的特點
優點:封裝性好,、複用性好、屏蔽細節、便於維護
缺點:繼承的侷限性
4.模板方法模式在項目中的應用
1.算法或操作遵循相似的邏輯
2.重構時,把相同的代碼抽取到父類中
3.重要、複雜的算法,核心算法設計爲模板算法
下面就通過寫一個銀行辦理業務的模板模式:
首先是一個抽象基類:
BankOption.java
public abstract class BankOption {
protected String name;
public BankOption(String name){
this.name = name;
}
public void templete(){
//1.辦理業務
business();
//2.填寫單據
writeBills();
//3.等待呼叫
callWait();
//4.窗口辦理
windowDeal();
}
/**
* 辦理業務
*/
private void business(){
System.out.println(name + "辦理業務");
}
/**
* 填寫單據,具體填寫說明單據,讓用戶自己選擇
*/
protected abstract void writeBills();
/**
* 等待呼叫
*/
private void callWait(){
System.out.println(name + "等待呼叫...");
//模擬顧客等待5秒鐘
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//5秒後等待結束,可以辦理業務了
System.out.println("呼叫" + name + "到窗口辦理業務");
}
/**
* 窗口辦理
*/
private void windowDeal(){
System.out.println(name + "窗口辦理");
System.out.println("窗口辦理中...");
//模擬在窗口辦理業務時3秒鐘
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "辦理業務完成");
}
}
Customer1.java
public class Customer1 extends BankOption{
public Customer1(String name){
super(name);
}
@Override
protected void writeBills() {
System.out.println(name + "填寫匯款單據");
}
}
Customer2.java
public class Customer2 extends BankOption{
public Customer2(String name){
super(name);
}
@Override
protected void writeBills() {
System.out.println(name + "填寫存款單據");
}
}
Customer3.java
public class Customer3 extends BankOption{
public Customer3(String name){
super(name);
}
@Override
protected void writeBills() {
System.out.println(name + "填寫辦理銀行卡業務");
}
}
Main.java測試類:
public class Main {
public static void main(String[] args) {
BankOption c1 = new Customer1("顧客1");
BankOption c2 = new Customer2("顧客2");
BankOption c3 = new Customer3("顧客3");
c1.templete();
System.out.println("\n*************************");
c2.templete();
System.out.println("*************************");
c3.templete();
}
}
運行結果:
顧客1辦理業務
顧客1填寫匯款單據
顧客1等待呼叫…
呼叫顧客1到窗口辦理業務
顧客1窗口辦理
窗口辦理中…
顧客1辦理業務完成
‘***************’
顧客2辦理業務
顧客2填寫存款單據
顧客2等待呼叫…
呼叫顧客2到窗口辦理業務
顧客2窗口辦理
窗口辦理中…
顧客2辦理業務完成
‘***************’
顧客3辦理業務
顧客3填寫辦理銀行卡業務
顧客3等待呼叫…
呼叫顧客3到窗口辦理業務
顧客3窗口辦理
窗口辦理中…
顧客3辦理業務完成
上就是一個簡單的銀行辦理業務的模板方法模式,我們把所以規定了的步奏,1、3、4都寫在基類中,這樣很好的隱藏了實現細節,顧客只需要填寫對應的單據就行了,那麼我們可以通過上面的例子再改進下,來理解下上面提到的細節:鉤子方法,爲什麼使用鉤子方法呢?上面我們每個用戶都模擬了等待呼叫功能,有時候如果另外一個窗口已經是空閒的,就可以直接叫用戶去辦理業務了,這個時候我們就可以使用鉤子方法了,當鉤子方法鉤上的時候,也就是在本程序中表示,你可以不用等待呼叫,直接去窗口辦理業務了,如果沒有鉤上,就表示你要繼續等待,這裏就模擬一下,1、3不等待,2等待。通過一下代碼比較一下。
BankOption.java中加入一個鉤子方法即可
public void templete(){
//1.辦理業務
business();
//2.填寫單據
writeBills();
//3.等待呼叫
if(isWait())
callWait();
else
System.out.println("不需要等待");
//4.窗口辦理
windowDeal();
}
//通過加入這個方法去判斷是否需要等待,當然,這裏有點不符合實際,
//因爲這裏是用戶去實現是否等待,一般來說我們是銀行來控制是否顧客等待,不過只要大家理解鉤子方法是什麼意思就可以了
protected abstract boolean isWait();
然後每個用戶去添加實現鉤子方法即可:
Customer1.java
@Override
protected boolean isWait() {
//false表示不需要等待,直接辦理窗口
return false;
}
Customer2.java
@Override
protected boolean isWait() {
//表示需要等待
return true;
}
Customer3.java
@Override
protected boolean isWait() {
//不需要等待
return false;
}
運行結果:
顧客1辦理業務
顧客1填寫匯款單據
不需要等待
顧客1窗口辦理
窗口辦理中…
顧客1辦理業務完成
***************’
顧客2辦理業務
顧客2填寫存款單據
顧客2等待呼叫…
呼叫顧客2到窗口辦理業務
顧客2窗口辦理
窗口辦理中…
顧客2辦理業務完成
***************’
顧客3辦理業務
顧客3填寫辦理銀行卡業務
不需要等待
顧客3窗口辦理
窗口辦理中…
顧客3辦理業務完成
模板方法模式就介紹到這裏,在我們的實際開發應用過程中,模板方法模式應用非常廣泛,所以這種設計模式大家一定得掌握。