模板方法模式-封裝一套算法流程

公號:碼農充電站pro
主頁:https://codeshellme.github.iohtml

今天來介紹模板方法模式Template Method Design Pattern)。java

1,製做飲料的過程

在這裏插入圖片描述

假如咱們要製做兩種飲料:蘋果飲料和橙子飲料。這兩種飲料的製做流程以下:git

  • 蘋果飲料製做流程
    • 把蘋果榨成蘋果汁
    • 將蘋果汁倒入杯中
    • 向杯中倒入水
    • 根據客戶喜愛是否放入白糖
      • 喜歡則放入白糖,不然不放白糖
  • 橙子飲料製做流程
    • 把橙子榨成橙汁
    • 將橙汁倒入杯中
    • 向杯中倒入水
    • 根據客戶喜愛是否放入白糖
      • 喜歡則放入白糖,不然不放白糖

2,模擬製做飲料

若是要模擬飲料的製做過程,按照最直接的想法,咱們建立兩個類 AppleBeverageOrangeBeverage 分別用於製做蘋果飲料和橙子飲料。github

首先根據蘋果飲料的製做流程編寫 AppleBeverage 類:算法

class AppleBeverage {
    private boolean isSweet;

    public AppleBeverage(boolean isSweet) {
        this.isSweet = isSweet;
    }

    // 把蘋果榨成蘋果汁
    public void squeezeAppleJuice() {
        System.out.println("squeeze apple juice");
    }

    // 將蘋果汁倒入杯中
    public void appleJuiceToCup() {
        System.out.println("pour the apple juice into the cup");
    }

    // 向杯中倒入水
    public void waterToCup() {
        System.out.println("pour water into the cup");
    }

    // 向杯中倒入白糖
    public void sugarToCup() {
        System.out.println("pour sugar into the cup");
    }

    // 製做蘋果飲料
    public void makeAppleBeverage() {
        squeezeAppleJuice();
        appleJuiceToCup();
        waterToCup();

        if (isSweet) {
            sugarToCup();
        }
    }
}

再根據橙子飲料的製做流程編寫 OrangeBeverage 類:shell

class OrangeBeverage {
    private boolean isSweet;

    public OrangeBeverage(boolean isSweet) {
        this.isSweet = isSweet;
    }

    // 把橙子榨成橙汁
    public void squeezeOrangeJuice() {
        System.out.println("squeeze orange juice");
    }

    // 將橙汁倒入杯中
    public void orangeJuiceToCup() {
        System.out.println("pour the orange juice into the cup");
    }

    // 向杯中倒入水
    public void waterToCup() {
        System.out.println("pour water into the cup");
    }

    // 向杯中倒入白糖
    public void sugarToCup() {
        System.out.println("pour sugar into the cup");
    }

    // 製做橙子飲料
    public void makeOrangeBeverage() {
        squeezeOrangeJuice();
        orangeJuiceToCup();
        waterToCup();

        if (isSweet) {
            sugarToCup();
        }
    }
}

3,分析代碼

能夠看到上面兩個類的代碼很是簡單,爲了更加詳細的分析,我畫出了這兩個類的類圖:app

在這裏插入圖片描述

我將這兩個類中的方法相同的部分用藍色標了出來,能夠看到,這兩個類中的 waterToCupsugarToCup 方法如出一轍,其它三個方法也是很是的類似。框架

這樣的代碼顯然是沒有複用已有的代碼。ui

4,改進代碼

那麼,天然而然,咱們能夠將兩個類中相同的部分,抽象出來放入一個父類中,而後不一樣的部分讓子類去實現。this

所以,咱們能夠編寫出父類,以下:

abstract class Beverage {
    protected boolean isSweet;

    // 榨果汁
    public abstract void squeezeJuice();
    
    // 將果汁倒入杯中
    public abstract void juiceToCup();
    
    // 向杯中倒入水
    public void waterToCup() {
        System.out.println("pour water into the cup");
    }

    // 向杯中倒入白糖
    public void sugarToCup() {
        System.out.println("pour sugar into the cup");
    }

    // 製做蘋果飲料
    public final void makeBeverage() {
        squeezeJuice();
        juiceToCup();
        waterToCup();
        
        // 根據喜愛是否加白糖
        if (isSweet) {
            sugarToCup();
        }
    }
}

咱們將全部相同的代碼都抽取到了 Beverage 類中,相同的部分有:

  • isSweet 變量
  • waterToCup 方法
  • sugarToCup 方法
  • makeBeverage 方法

其中 makeBeverage 方法使用了 final 關鍵字來修飾,表示咱們不但願子類去修改它。

不一樣的部分有:

  • squeezeJuice 方法
  • juiceToCup 方法

這兩個方法都是抽象方法,表示咱們但願子類根據本身的需求去實現。最終在子類中調用 makeBeverage 方法時,makeBeverage 會依據多態性來調用正確的 squeezeJuicejuiceToCup 方法。

下面編寫 AppleBeverage 類:

class AppleBeverage extends Beverage {
    public AppleBeverage(boolean isSweet) {
        this.isSweet = isSweet;
    }

    public void squeezeJuice() {
        System.out.println("squeeze apple juice");
    }

    public void juiceToCup() {
        System.out.println("pour the apple juice into the cup");
    }
}

AppleBeverage 繼承了 Beverage,而且實現了 squeezeJuicejuiceToCup 方法。

再編寫 OrangeBeverage 類:

class OrangeBeverage extends Beverage {
    public OrangeBeverage(boolean isSweet) {
        this.isSweet = isSweet;
    }

    public void squeezeJuice() {
        System.out.println("squeeze orange juice");
    }

    public void juiceToCup() {
        System.out.println("pour the orange juice into the cup");
    }
}

OrangeBeverage 繼承了 Beverage,而且實現了 squeezeJuicejuiceToCup 方法。

通過改進後的代碼類圖以下:

在這裏插入圖片描述

能夠看到通過改進的代碼,重複的代碼都抽取到了父類中,能複用的代碼都進行了複用,子類只需根據本身的須要實現父類的抽象方法就行。

我將全部代碼放在了這裏,供你們參考。

5,模板方法

實際上,上面代碼的實現方式就使用到了模板方法模式

模板方法模式在一個方法中定義一個算法的骨架,而將一些步驟延遲到子類中,使得子類能夠在不改變算法結構的狀況下,從新定義算法中的某些步驟

這裏的算法指的是實際項目中的業務邏輯

模板方法的類圖很簡單,以下:

在這裏插入圖片描述

模板方法模式的重點在於,它在父類中定義了一個通用的算法框架templateMethod),也就是上面代碼中的 makeBeverage 方法,這個方法就是模板方法,通常用 final 修飾,用於防止子類覆蓋。

另外還有一些抽象方法,這些抽象方法都用 abstract 進行了修飾,表示必須由子類實現。算法框架調用了這些抽象方法,這樣就至關於子類從新定義了算法中的某些步驟。

在上面代碼的 makeBeverage 方法中還用到了一個變量 isSweet,這個變量在子類對象中的不一樣取值,會影響到 makeBeverage 的執行流程。

這個 isSweet 變量叫做「鉤子」,鉤子能夠是一個變量,也能夠是一個方法,它能夠改變模板方法的執行流程。

6,總結

模板方法模式提供了一個算法步驟,從中咱們能看到代碼複用的技巧。

模板方法模式中的抽象方法由子類實現,這意味着父類定義了算法的框架流程,而將算法的實現延遲到了子類中。

咱們一般會將模板方法工廠方法放在一塊兒比較,這兩個模式有一個明顯的不一樣點,就是模板方法模式將一個算法流程中的某些步驟的具體實現延遲到了子類中,而工廠方法模式是將對象的建立延遲到了子類中。

Java JDK 中,咱們能看到不少模板方法的應用案例,好比 InputStream.read(byte b[], int off, int len) 方法。

(本節完。)


推薦閱讀:

觀察者模式-將消息通知給觀察者

裝飾者模式-動態的包裝原有對象的行爲

命令模式-將請求封裝成對象

適配器模式-讓不兼容的接口得以適配

外觀模式-簡化子系統的複雜性


歡迎關注做者公衆號,獲取更多技術乾貨。

碼農充電站pro

相關文章
相關標籤/搜索