前言
模板方法可以認爲是23種設計模式中最簡單的一種了,並且生活中能找到很多的場景。模板方法雖然簡單但是有些細節我們還是不能忽視的。
定義
-
模板方法是一種行爲類設計模式。 -
模板方法是一個定義在父類的方法,在模板方法中會調用多個定義在父類的其他方法,而這些方法有可能只是抽象方法並沒有實現。 -
模板方法僅決定這些抽象方法的執行順序,這些抽象方法的實現由子類負責,並且子類不允許重寫模板方法。
應用場景
-
多個子類有公共的方法,並且邏輯相同。 -
重要,複雜的算法,可以把核心算法設計爲模板方法。
優點
-
封裝不變部分,擴展可變部分。 -
提取公共部分代碼,便於維護。 -
行爲由父類控制,子類實現。
把認爲是不變部分的算法封裝到父類的實現,而可變部分的則可以通過繼承來繼續擴展。基本方法是由子類實現的。因此子類可以通過擴展的方式增加相應功能,符合開閉原則。
缺點
-
對每個不同的實現都需要定義一個子類,這會導致類的個數增加,系統更加龐大,設計也更加抽象。 -
父類中的抽象方法由子類實現,子類執行的結果會影響父類的結果,這導致一種反向的控制結構,它提高了代碼閱讀的難度。
UML模型
實例代碼
場景:我相信每個人每天起牀都會經歷: 起牀 -> 刷牙 -> 喫早餐 -> 上班 這樣幾個過程,但是每個人的過程又是不一樣的。
基礎的日常類
public abstract class BaseDaily {
/**
* 起牀
*/
protected abstract void getUp();
/**
* 刷牙
*/
protected abstract void brushTeeth();
/**
* 喫早餐
*/
protected abstract void eatBreakfast();
/**
* 去上班
*/
protected abstract void goForWork();
/**
* 開始美好的一天
*/
final void start(){
getUp();
brushTeeth();
eatBreakfast();
goForWork();
}
}
Andy的日常
public class AndyDaily extends BaseDaily {
@Override
protected void getUp() {
System.out.println("andy起牀了");
}
@Override
protected void brushTeeth() {
System.out.println("andy用米家牙刷刷牙");
}
@Override
protected void eatBreakfast() {
System.out.println("andy的早餐是油條加豆漿");
}
@Override
protected void goForWork() {
System.out.println("andy走路去上班");
}
}
Bob的日常
public class BobDaily extends BaseDaily {
@Override
protected void getUp() {
System.out.println("bob起牀了");
}
@Override
protected void brushTeeth() {
System.out.println("bob用飛利浦牙刷刷牙");
}
@Override
protected void eatBreakfast() {
System.out.println("bob的早餐是腸粉");
}
@Override
protected void goForWork() {
System.out.println("bob坐地鐵去上班");
}
}
Jack的日常
public class JackDaily extends BaseDaily {
@Override
protected void getUp() {
System.out.println("jack起牀了");
}
@Override
protected void brushTeeth() {
System.out.println("jack用網易牙刷刷牙");
}
@Override
protected void eatBreakfast() {
System.out.println("jack的早餐是拌粉");
}
@Override
protected void goForWork() {
System.out.println("jack開車去上班");
}
}
測試
public class Test {
public static void main(String[] args) {
BaseDaily andyDaily = new AndyDaily();
andyDaily.start();
System.out.println("====================");
BaseDaily jackDaily = new JackDaily();
jackDaily.start();
System.out.println("====================");
BaseDaily bobDaily = new BobDaily();
bobDaily.start();
}
}
運行結果
andy起牀了
andy用米家牙刷刷牙
andy的早餐是油條加豆漿
andy走路去上班
====================
jack起牀了
jack用網易牙刷刷牙
jack的早餐是拌粉
jack開車去上班
====================
bob起牀了
bob用飛利浦牙刷刷牙
bob的早餐是腸粉
bob坐地鐵去上班
模板方法擴展
場景:我相信每個人每天起牀都會經歷: 起牀 -> 刷牙 -> 喫早餐 -> 上班 這樣幾個步驟,但是每個人又是不一樣的。但是可能有人,起牀晚了,就沒時間喫早餐了。那這個時候我們該咋辦呢?
解決方案如下
修改 BaseDaily 添加一個是否有時間喫早餐的方法,讓自身判斷有沒有時間喫早餐。同時子類需要增加相應的實現。
BaseDaily
public abstract class BaseDaily {
/**
* 起牀
*/
protected abstract void getUp();
/**
* 刷牙
*/
protected abstract void brushTeeth();
/**
* 喫早餐
*/
protected abstract void eatBreakfast();
/**
* 去上班
*/
protected abstract void goForWork();
/**
* 是否有時間喫早餐
* @return 返回 true 或者 false
*/
protected abstract boolean isHaveTimeEatBreakfast();
/**
* 開始美好的一天
*/
final void start(){
getUp();
brushTeeth();
if(isHaveTimeEatBreakfast()){
eatBreakfast();
}
goForWork();
}
}
Andy日常
public class AndyDaily extends BaseDaily {
@Override
protected void getUp() {
System.out.println("andy起牀了");
}
@Override
protected void brushTeeth() {
System.out.println("andy用米家牙刷刷牙");
}
@Override
protected void eatBreakfast() {
System.out.println("andy的早餐是油條加豆漿");
}
@Override
protected void goForWork() {
System.out.println("andy走路去上班");
}
@Override
protected boolean isHaveTimeEatBreakfast() {
return false;
}
}
Bob日常
public class BobDaily extends BaseDaily {
@Override
protected void getUp() {
System.out.println("bob起牀了");
}
@Override
protected void brushTeeth() {
System.out.println("bob用飛利浦牙刷刷牙");
}
@Override
protected void eatBreakfast() {
System.out.println("bob的早餐是腸粉");
}
@Override
protected void goForWork() {
System.out.println("bob坐地鐵去上班");
}
@Override
protected boolean isHaveTimeEatBreakfast() {
return true;
}
}
Jack日常
public class JackDaily extends BaseDaily {
@Override
protected void getUp() {
System.out.println("jack起牀了");
}
@Override
protected void brushTeeth() {
System.out.println("jack用網易牙刷刷牙");
}
@Override
protected void eatBreakfast() {
System.out.println("jack的早餐是拌粉");
}
@Override
protected void goForWork() {
System.out.println("jack開車去上班");
}
@Override
protected boolean isHaveTimeEatBreakfast() {
return true;
}
}
測試
public class Test {
public static void main(String[] args) {
BaseDaily andyDaily = new AndyDaily();
andyDaily.start();
System.out.println("====================");
BaseDaily jackDaily = new JackDaily();
jackDaily.start();
System.out.println("====================");
BaseDaily bobDaily = new BobDaily();
bobDaily.start();
}
}
運行結果如下: 由於andy起牀太晚就沒有時間喫早餐了。
andy起牀了
andy用米家牙刷刷牙
andy走路去上班
====================
jack起牀了
jack用網易牙刷刷牙
jack的早餐是拌粉
jack開車去上班
====================
bob起牀了
bob用飛利浦牙刷刷牙
bob的早餐是腸粉
bob坐地鐵去上班
鉤子方法
在我們的抽象類中isHaveTimeEatBreakfast()的返回值就是影響了模板方法的執行結果,該方法就叫做鉤子方法(HookMethod)。
需要注意的點
-
爲了子類防止惡意的操作,一般模板方法都加上final關鍵字,不允許被重寫。 -
抽象模板中的基本方法儘量設計爲protected類型,符合迪米特法則,不需要暴露的屬性或方法儘量不要設置爲protected類型。實現類若非必要,儘量不要擴大父類中的訪問權限。
參考書籍
-
設計模式之禪道第二版
結尾
如果覺得對你有幫助,可以多多評論,多多點贊哦,也可以到我的主頁看看,說不定有你喜歡的文章,可以隨手點個關注哦,謝謝。
我是不一樣的科技宅,每天進步一點點,體驗不一樣的生活。我們下期見!
本文分享自微信公衆號 - 不一樣的科技宅(byydkjz)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。