1. 程式人生 > >一個最簡單的設計模式-模板方法

一個最簡單的設計模式-模板方法

《Head First設計模式》已經讀了不止一遍,但是始終沒有進行系統的進行總結。所以近期開始總結設計模式相關的知識,從模板方法模式開始,因為是一個我認為是最簡單的設計模式。(推薦視訊資源23個設計模式)

提出&解決問題

提出問題

實現製作咖啡功能。且製作咖啡需要四個步驟 :

  1. 燒水
  2. 沖泡咖啡
  3. 倒入杯中
  4. 加糖

程式碼實現

/**
 * 一杯加糖咖啡
 *
 * @author Jann Lee
 * @date 2019-07-14 18:37
 */
public class Coffee {

    /**
     * 製作一杯加糖咖啡
     */
    public void prepareRecipe() {
        boilWater();
        steepTeaBag();
        portInCup();
        addLemon();
    }

    /**
     * step1: 燒水
     */
    private void boilWater() {
        System.out.println("燒水...");
    }

    /**
     * step2:沖泡咖啡
     */
    private void steepTeaBag() {
        System.out.println("沖泡咖啡...");
    }

    /**
     * step3: 倒入杯中
     */
    private void portInCup() {
        System.out.println("倒入杯中...");
    }

    /**
     * step4: 加糖
     */
    private void addLemon() {
        System.out.println("加糖...");
    }

再次提出問題此時此刻我需要一杯檸檬茶呢?【燒水,沖泡茶包,倒入杯中,加檸檬】

這個問題當然很簡單,我們只需要如法炮製即可。

public class Tea {

    /**
     * 製作一杯檸檬茶
     */
    public void prepareRecipe(){
        boilWater();
        brewCoffeeGrinds();
        portInCup();
        addSugarAndMilk();
    }

    /**
     * step1: 燒水
     */
    private void boilWater() {
        System.out.println("燒水...");
    }

    /**
     * step2:沖泡咖啡
     */
    private void brewCoffeeGrinds() {
        System.out.println("沖泡茶包...");
    }

    /**
     * step3: 倒入杯中
     */
    private void portInCup() {
        System.out.println("倒入杯中...");
    }

    /**
     * step4: 加檸檬
     */
    private void addSugarAndMilk() {
        System.out.println("加入檸檬片...");
    }
}

思考

​ 如果此時我們又需要一杯不加檸檬的茶,加奶的咖啡...,當然我們可以按照上面方式重新依次實現即可。但是如果你是一個有經驗的程式設計師,或者你學習過設計模式。你可能會發現以上功能實現的步驟/流程固定,當需求發生變化時,只有小部分步驟有所改變。

優化程式碼

根據面向物件程式的特點,既抽象,封裝,繼承,多型。我們可以對程式碼進行抽象,將公共程式碼提取到基類。我們將咖啡和茶抽象成咖啡因飲料,將其中相同的兩步,燒水和倒入杯中再父類中實現,將沖泡和新增調料延遲到子類。

  1. 定義一個基類
public abstract class CafeineBeverage {
    /**
     * 製作一杯咖啡因飲料
     */
    public void prepareRecipe() {
        boilWater();
        brew();
        portInCup();
        addCondiments();
    }

    /**
     * step1: 燒水
     */
    private void boilWater() {
        System.out.println("燒水...");
    }

    /**
     * step2:沖泡
     */
    protected abstract void brew();

    /**
     * step3: 入杯中
     */
    private void portInCup() {
        System.out.println("倒入杯中...");
    }

    /**
     * step4: 加調料
     */
    protected abstract void addCondiments();
}
// 一杯加糖咖啡
public class CoffeeBeverage extends CafeineBeverage{

    @Override
    protected void brew() {
        System.out.println("沖泡咖啡...");
    }

    @Override
    protected void addCondiments() {
        System.out.println("加糖...");
    }
}
// 一杯檸檬茶
public class TeaBeverage extends CafeineBeverage {
    @Override
    protected void brew() {
        System.out.println("沖泡茶包...");
    }

    @Override
    protected void addCondiments() {
        System.out.println("加檸檬...");
    }
}

模板方法模式

如果按以上方式對程式碼進行了優化,其實就實現了模板方法模式。一下是模板方法模式相關概念。

動機

  • 在軟體構建過程中,對於某一項任務,它常常有穩定的整體操作結構,但是各個子步驟卻有很多改變的需求,或者由於固有的原因(比如框架與應用之間的關係)而無法和任務的整體結構同時實現
  • 如何在確定穩定的操作結構的前提下,來靈活應對各個子步驟的變化或者晚期實現需求?

定義

定義一個操作中演算法的骨架(穩定),而將一些步驟延遲(變化)到子類。Template Method使得子類可以不改變(複用)一個演算法的結構,即可重新定義(override)該演算法的特定步驟。

要點總結

  • Template Method是一種非常基礎性的設計模式,在面向物件系統中,有著大量的應用。他用最簡潔的機制(抽象類的多型,為很多應用框架提供了靈活的擴充套件點,是程式碼複用方面最基本實現結構)
  • 除了可以靈活應對子步驟的變化外,“不要呼叫我,讓我來呼叫你”的反向控制結構是Template Method的典型應用
  • 在具體實現方面,被Template Method呼叫得虛方法可以有實現,也可以沒有實現(抽象方法),但一般推薦設定為protected方法

類圖:

個人部落格網站(正在建設中)