設計模式-模板方法

設計模式-模板方法

前言

  大家好我是不一樣的科技宅,每天進步一點點,體驗不一樣的生活。今天我們聊一聊模板方法模式,模板方法可以認為是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類型。實現類若非必要,儘量不要擴大父類中的訪問權限。

參考書籍

  • 設計模式之禪道第二版

結尾

  如果覺得對你有幫助,可以多多評論,多多點贊哦,也可以到我的主頁看看,說不定有你喜歡的文章,可以隨手點個關注哦,謝謝。


分享到:


相關文章: