公號:碼農充電站pro
主頁:https://codeshellme.github.iohtml
今天來介紹模板方法模式(Template Method Design Pattern)。java
假如咱們要製做兩種飲料:蘋果飲料和橙子飲料。這兩種飲料的製做流程以下:git
若是要模擬飲料的製做過程,按照最直接的想法,咱們建立兩個類 AppleBeverage 和 OrangeBeverage 分別用於製做蘋果飲料和橙子飲料。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(); } } }
能夠看到上面兩個類的代碼很是簡單,爲了更加詳細的分析,我畫出了這兩個類的類圖:app
我將這兩個類中的方法相同的部分用藍色標了出來,能夠看到,這兩個類中的 waterToCup
和 sugarToCup
方法如出一轍,其它三個方法也是很是的類似。框架
這樣的代碼顯然是沒有複用已有的代碼。ui
那麼,天然而然,咱們能夠將兩個類中相同的部分,抽象出來放入一個父類中,而後不一樣的部分讓子類去實現。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
會依據多態性來調用正確的 squeezeJuice
和 juiceToCup
方法。
下面編寫 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,而且實現了 squeezeJuice
和 juiceToCup
方法。
再編寫 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,而且實現了 squeezeJuice
和 juiceToCup
方法。
通過改進後的代碼類圖以下:
能夠看到通過改進的代碼,重複的代碼都抽取到了父類中,能複用的代碼都進行了複用,子類只需根據本身的須要實現父類的抽象方法就行。
我將全部代碼放在了這裏,供你們參考。
實際上,上面代碼的實現方式就使用到了模板方法模式。
模板方法模式在一個方法中定義一個算法的骨架,而將一些步驟延遲到子類中,使得子類能夠在不改變算法結構的狀況下,從新定義算法中的某些步驟。
這裏的算法指的是實際項目中的業務邏輯。
模板方法的類圖很簡單,以下:
模板方法模式的重點在於,它在父類中定義了一個通用的算法框架(templateMethod),也就是上面代碼中的 makeBeverage
方法,這個方法就是模板方法,通常用 final
修飾,用於防止子類覆蓋。
另外還有一些抽象方法,這些抽象方法都用 abstract
進行了修飾,表示必須由子類實現。算法框架調用了這些抽象方法,這樣就至關於子類從新定義了算法中的某些步驟。
在上面代碼的 makeBeverage
方法中還用到了一個變量 isSweet
,這個變量在子類對象中的不一樣取值,會影響到 makeBeverage
的執行流程。
這個 isSweet
變量叫做「鉤子」,鉤子能夠是一個變量,也能夠是一個方法,它能夠改變模板方法的執行流程。
模板方法模式提供了一個算法步驟,從中咱們能看到代碼複用的技巧。
模板方法模式中的抽象方法由子類實現,這意味着父類定義了算法的框架流程,而將算法的實現延遲到了子類中。
咱們一般會將模板方法與工廠方法放在一塊兒比較,這兩個模式有一個明顯的不一樣點,就是模板方法模式將一個算法流程中的某些步驟的具體實現延遲到了子類中,而工廠方法模式是將對象的建立延遲到了子類中。
在 Java JDK 中,咱們能看到不少模板方法的應用案例,好比 InputStream.read(byte b[], int off, int len)
方法。
(本節完。)
推薦閱讀:
歡迎關注做者公衆號,獲取更多技術乾貨。