前言
大家好我是不一樣的科技宅,每天進步一點點,體驗不一樣的生活。今天我們聊一聊模板方法模式,模板方法可以認為是23種設計模式中最簡單的一種了,雖然簡單但是有些細節我們還是不能忽視的。
定義
- 模板方法是一種行為類設計模式。
- 模板方法是一個定義在父類的方法,在模板方法中會調用多個定義在父類的其他方法,而這些方法有可能只是抽象方法並沒有實現。
- 模板方法僅決定這些抽象方法的執行順序,這些抽象方法的實現由子類負責,並且子類不允許重寫模板方法。
應用場景
- 多個子類有公共的方法,並且邏輯相同。
- 重要,複雜的算法,可以把核心算法設計為模板方法。
優點
- 封裝不變部分,擴展可變部分。
- 提取公共部分代碼,便於維護。
- 行為由父類控制,子類實現。
把認為是不變部分的算法封裝到父類的實現,而可變部分的則可以通過繼承來繼續擴展。基本方法是由子類實現的。因此子類可以通過擴展的方式增加相應功能,符合開閉原則。
缺點
- 對每個不同的實現都需要定義一個子類,這會導致類的個數增加,系統更加龐大,設計也更加抽象。
- 父類中的抽象方法由子類實現,子類執行的結果會影響父類的結果,這導致一種反向的控制結構,它提高了代碼閱讀的難度。
UML模型
實例代碼
場景:我相信每個人每天起床都會經歷: 起床 -> 刷牙 -> 吃早餐 -> 上班 這樣幾個過程,但是每個人的過程又是不一樣的。
基礎的日常類
<code>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();
}
}/<code>
Andy的日常
<code>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走路去上班");
}
}/<code>
Bob的日常
<code>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坐地鐵去上班");
}
}/<code>
Jack的日常
<code>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開車去上班");
}
}/<code>
測試
<code>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();
}
}/<code>
運行結果
<code>andy起床了
andy用米家牙刷刷牙
andy的早餐是油條加豆漿
andy走路去上班
====================
jack起床了
jack用網易牙刷刷牙
jack的早餐是拌粉
jack開車去上班
====================
bob起床了
bob用飛利浦牙刷刷牙
bob的早餐是腸粉
bob坐地鐵去上班/<code>
模板方法擴展
場景:我相信每個人每天起床都會經歷: 起床 -> 刷牙 -> 吃早餐 -> 上班 這樣幾個步驟,但是每個人又是不一樣的。但是可能有人,起床晚了,就沒時間吃早餐了。那這個時候我們該咋辦呢?
解決方案如下
修改 BaseDaily 添加一個是否有時間吃早餐的方法,讓自身判斷有沒有時間吃早餐。同時子類需要增加相應的實現。
BaseDaily
<code>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();
}
}/<code>
Andy日常
<code>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;
}
}/<code>
Bob日常
<code>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;
}
}/<code>
Jack日常
<code>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;
}
}/<code>
測試
<code>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();
}
}/<code>
運行結果如下: 由於andy起床太晚就沒有時間吃早餐了。
<code>andy起床了
andy用米家牙刷刷牙
andy走路去上班
====================
jack起床了
jack用網易牙刷刷牙
jack的早餐是拌粉
jack開車去上班
====================
bob起床了
bob用飛利浦牙刷刷牙
bob的早餐是腸粉
bob坐地鐵去上班/<code>
鉤子方法
在我們的抽象類中isHaveTimeEatBreakfast()的返回值就是影響了模板方法的執行結果,該方法就叫做鉤子方法(HookMethod)。
需要注意的點
- 為了子類防止惡意的操作,一般模板方法都加上final關鍵字,不允許被重寫。
- 抽象模板中的基本方法儘量設計為protected類型,符合迪米特法則,不需要暴露的屬性或方法儘量不要設置為protected類型。實現類若非必要,儘量不要擴大父類中的訪問權限。
參考書籍
- 設計模式之禪道第二版
結尾
如果覺得對你有幫助,可以多多評論,多多點贊哦,也可以到我的主頁看看,說不定有你喜歡的文章,可以隨手點個關注哦,謝謝。
閱讀更多 不一樣的科技宅 的文章