設計模式-從你的日常理解模板方法


前言

  模板方法可以認爲是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源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章