設(shè)計模式-模板方法模式

關(guān)注公眾號 MageByte,設(shè)置星標(biāo)獲取最新干貨。公眾號后臺回復(fù) “加群” 進(jìn)入技術(shù)交流群獲更多技術(shù)成長。
——本文由 MageByte 團(tuán)隊的「青葉」編寫

模板方法模式在一個方法中定義了一個算法骨架,并且 final 修飾防止子類重寫。方法中包含一些抽象方法,也就是一些步驟延遲到字類實現(xiàn)。模板方法使得在不改變算法結(jié)構(gòu)的情況下,重新定義算法中的某些步驟。完整代碼可以查看GitHub:https://github.com/UniqueDong/zero-design-stu

類圖

模板方法模式

模式實現(xiàn)

在實現(xiàn)模板方法模式時,開發(fā)抽象類的軟件設(shè)計師和開發(fā)具體子類的軟件設(shè)計師之間可以進(jìn)行協(xié)作。一個設(shè)計師負(fù)責(zé)給出一個算法的輪廓和框架,另一些設(shè)
計師則負(fù)責(zé)給出這個算法的各個邏輯步驟。實現(xiàn)這些具體邏輯步驟的方法即為基本方法,而將這些基本方法匯總起來的方法即為模板方法,模板方法模式的
名字也因此而來。下面將詳細(xì)介紹模板方法和基本方法:

  1. 模板方法
    一個模板方法是定義在抽象類中的、把基本操作方法組合在一起形成一個總算法或一個總行為的方法。這個模板方法定義在抽象類中,并由子類不加以修改
    地完全繼承下來。模板方法是一個具體方法,它給出了一個頂層邏輯框架,而邏輯的組成步驟在抽象類中可以是具體方法,也可以是抽象方法。由于模板方法
    是具體方法,因此模板方法模式中的抽象層只能是抽象類,而不是接口。
  2. 基本方法
    基本方法是實現(xiàn)算法各個步驟的方法,是模板方法的組成部分?;痉椒ㄓ挚梢苑譃槿N:抽象方法(Abstract Method)、具體方法(Concrete Method)和鉤子方法(Hook Method)。
    (1) 抽象方法:一個抽象方法由抽象類聲明、由其具體子類實現(xiàn)。
    (2) 具體方法:一個具體方法由一個抽象類或具體類聲明并實現(xiàn),其子類可以進(jìn)行覆蓋也可以直接繼承。
    (3) 鉤子方法:一個鉤子方法由一個抽象類或具體類聲明并實現(xiàn),而其子類可能會加以擴(kuò)展。通常在父類中給出的實現(xiàn)是一個空實現(xiàn),并以該空實
    現(xiàn)作為方法的默認(rèn)實現(xiàn),當(dāng)然鉤子方法也可以提供一個非空的默認(rèn)實現(xiàn)。
    鉤子可以讓子類實現(xiàn)算法中可選的部分,或者在鉤子對于子類的實現(xiàn)并不重要的時候,子類可以對此鉤子置之不理。鉤子的另一個用法,是讓子類能夠有機(jī)會
    對模板方法中某些即將發(fā)生的(或剛剛發(fā)生的)步驟做出反應(yīng)。

使用場景

開一家咖啡、茶館,泡茶和咖啡的沖泡方式非常相似:

星巴克咖啡沖泡法

  1. 把水煮沸
  2. 用沸水沖泡咖啡
  3. 把咖啡倒進(jìn)杯子
  4. 加糖和牛奶

功夫茶沖泡法

  1. 把水煮沸
  2. 用沸水沖泡茶葉
  3. 把茶倒進(jìn)杯子
  4. 加檸檬

我們可以發(fā)現(xiàn)兩種茶的步驟1,和步驟3是一樣的。整體算法結(jié)構(gòu)是固定的,只是有的部分不一樣。這時候我們就可以使用模板方法設(shè)計模式定義制作骨架,然后部分細(xì)節(jié)留給子類實現(xiàn)。

代碼實現(xiàn)

首先我們先抽象一個制作飲料的模板,定義算法邏輯 AbstractBeverage。同時有一個鉤子方法,一般是空實現(xiàn),在這里我們可以通過它(customerWantsCondiments())來控制是否加調(diào)料。

package com.zero.design.actions.template;

/**
 * 抽象制作飲料模板:定義算法骨架
 */
public abstract class AbstractBeverage {
    /**
     * 這就是模板方法。它被聲明為final,以免子類改變這個算法的順序。
     * 算法步驟組合
     */
    final void prepareRecipe() {
        // 模板方法定義了一連串的步驟,每個步驟由一個方法代表
        boilWater();
        brew();
        pourInCup();
        if (customerWantsCondiments()) {
            addCondiments();
        }
    }

    /**
     *  我們在這里定義了一個方法,(通常)是空的缺省實現(xiàn)。這個方法只會返回true,不做別的事。
     * 這就是一個鉤子,子類可以覆蓋這個方法,但不見得一定要這么。
     * @return
     */
    boolean customerWantsCondiments() {
        return true;
    }

    /**
     * 添加佐料:不同飲料也有不同佐料:申明為抽象類,由子類取操心
     */
    protected abstract void addCondiments();
    /**
     * 釀制:不同飲料方式也不同,申明為抽象類,由子類取操心
     */
    protected abstract void brew();

    /**
     * 共通方法:倒入杯中
     */
    public void pourInCup() {
        System.out.println("倒入杯子中...");
    }

    /**
     * 把水煮沸,共通方法
     */
    public void boilWater() {
        System.out.println("把水煮沸...");
    }
}

接著我們定義泡茶具體算法,并且繼承 AbstractBeverage 抽象算法,實現(xiàn)泡茶的具體邏輯。

package com.zero.design.actions.template;

public class Tea extends AbstractBeverage {

    /**
     * 這樣通過鉤子就可以選擇是都要加佐料了
     */
    private boolean addCondiments = true;

    /**
     * 添加糖、牛奶
     */
    @Override
    protected void addCondiments() {
        System.out.println("添加檸檬,茶更好喝");
    }

    /**
     * 咖啡沖泡方法
     */
    @Override
    protected void brew() {
        System.out.println("秘制泡茶方式放入茶葉");
    }

    /**
     * 使用鉤子,不加佐料
     * @return
     */
    @Override
    boolean customerWantsCondiments() {
        return addCondiments;
    }

    public boolean isAddCondiments() {
        return addCondiments;
    }

    public void setAddCondiments(boolean addCondiments) {
        this.addCondiments = addCondiments;
    }
}


定義咖啡的算法細(xì)節(jié)

package com.zero.design.actions.template;

/**
 * 咖啡具體實現(xiàn):只需要自行處理沖泡和添加調(diào)料部分
 */
public class Coffe extends AbstractBeverage {

    /**
     * 這樣通過鉤子就可以選擇是都要加佐料了
     */
    public boolean addCondiments = true;


    /**
     * 添加糖、牛奶
     */
    @Override
    protected void addCondiments() {
        System.out.println("添加糖跟牛奶");
    }

    /**
     * 咖啡沖泡方法
     */
    @Override
    protected void brew() {
        System.out.println("放入咖啡豆,使用秘制方法沖泡");
    }

    /**
     * 重寫鉤子
     * @return
     */
    @Override
    boolean customerWantsCondiments() {
        return addCondiments;
    }

    public boolean isAddCondiments() {
        return addCondiments;
    }

    public void setAddCondiments(boolean addCondiments) {
        this.addCondiments = addCondiments;
    }
}

接著就是客戶點單,我們通過模板方法模式制作咖啡或者功夫茶。達(dá)到代碼復(fù)用。

package com.zero.design.actions.template;

/**
 * Created by unique on 2017/6/7.
 */
public class Test {

    public static void main(String[] args) {
        Tea tea = new Tea();
        tea.setAddCondiments(false);
        tea.prepareRecipe();
        System.out.println("-------------------");
        Coffe coffe = new Coffe();
        coffe.prepareRecipe();
    }

}

輸出如下

把水煮沸...
秘制泡茶方式放入茶葉
倒入杯子中...
-------------------
把水煮沸...
放入咖啡豆,使用秘制方法沖泡
倒入杯子中...
添加糖跟牛奶

歡迎加群與我們分享,我們第一時間反饋。關(guān)注公眾號,后臺回復(fù) 加群即可獲取個人微信。


JavaStorm
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容