一、概述 模板方法模式在一個方法中定義一個演算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以在不改變演算法結構的情況下,重新定義演算法中的某些步驟。 二、結構類圖 三、解決問題 模板方法就是提供一個演算法框架,框架裡面的步驟有些是父類已經定好的,有些需要子類自己實現。相當於要去辦一件事情,行動的流 ...
一、概述
模板方法模式在一個方法中定義一個演算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以在不改變演算法結構的情況下,重新定義演算法中的某些步驟。
二、結構類圖
三、解決問題
模板方法就是提供一個演算法框架,框架裡面的步驟有些是父類已經定好的,有些需要子類自己實現。相當於要去辦一件事情,行動的流程已經定好了,但有些步驟需要自己去做,而有些步驟可能別人幫我們做了。就拿建網站來說,一般的程式是購買功能變數名稱-->購買空間-->上傳網站-->備案-->審核,每個網站的創建必須經過這樣的固定程式,但除了審核不用建站者關心,其他的步驟都要建站者自己去完成。
四、應用實例
現在我們很多家庭都有了豆漿機,豆漿的營養價值不用我多說了。製作豆漿的程式簡單點來說就是選材--->添加配料--->浸泡--->放到豆漿機打碎,通過添加不同的配料,可以製作出不同口味的豆漿,但是選材、浸泡和放到豆漿機打碎這幾個步驟對於製作每種口味的豆漿都是一樣的。
1、創建抽象類
package templatemethod.pattern; //豆漿類,抽象類 public abstract class SoyaMilk { //這是模板方法,用final修飾,不允許子類覆蓋。模板方法定義了製作豆漿的程式 final void prepareRecipe(){ selectMaterial(); addCondiments(); soak(); beat(); } //選材方法,選擇黃豆 void selectMaterial(){ System.out.println("第一步、選擇好了新鮮黃豆"); } //可以添加不同的配料,在這裡設置為抽象方法,子類必須實現 abstract void addCondiments(); //浸泡 void soak(){ System.out.println("第三步、黃豆和配料開始浸泡,大概需要5個小時"); } //放到豆漿機打碎 void beat(){ System.out.println("第四步、黃豆的配料放到豆漿機打碎"); } }
2、創建紅棗豆漿
package templatemethod.pattern; //紅棗豆漿 public class ReddatesSoyaMilk extends SoyaMilk{ //實現父類的添加配料方法 @Override void addCondiments() { System.out.println("第二步、添加紅棗配料"); } }
3、創建核桃豆漿
package templatemethod.pattern; //核桃豆漿 public class NutSoyaMilk extends SoyaMilk{ //實現父類的添加配料方法 @Override void addCondiments() { System.out.println("第二步、添加核桃配料"); } }
4、測試製作豆漿
package templatemethod.pattern; public class SoyaMilkTest { public static void main(String[] args){ //製作紅棗豆漿 System.out.println(); System.out.println("-----製作紅棗豆漿步驟-------"); SoyaMilk reddatesSoyaMilk = new ReddatesSoyaMilk(); reddatesSoyaMilk.prepareRecipe(); //製作核桃豆漿 System.out.println(); System.out.println("-----製作核桃豆漿步驟-------"); SoyaMilk nutSoyaMilk = new NutSoyaMilk(); nutSoyaMilk.prepareRecipe(); } }
運行結果:
五、優缺點
1、優點
(1)、演算法只存在於一個地方,也就是在父類中,容易修改。需要修改演算法時,只要修改父類的模板方法或者已經實現的某些步驟,子類就會繼承這些修改。
(2)、實現了最大化代碼復用。父類的模板方法和已實現的某些步驟會被子類繼承而直接使用。
(3)、既統一了演算法,也提供了很大的靈活性。父類的模板方法確保了演算法的結構保持不變,同時由子類提供部分步驟的實現。
(4)、提供了一個基本框架,容易擴展子類。模板方法有框架控制如何做事情,而由使用框架的人指定框架演算法中每個步驟的細節。子類只要繼承父類,實現抽象方法,就可以使用父類的演算法。
2、缺點
(1)、模板方法使用繼承方式復用代碼,如果要在基本演算法裡面增加一個步驟,而該步驟是抽象的話,每個子類都要修改代碼,實現這個步驟。
六、模板方法中的鉤子方法
在模板方法模式的父類中,我們可以定義一個方法,它預設不做任何事,子類可以視情況要不要覆蓋它,該方法稱為“鉤子”。我們還是用上面做豆漿的例子來講解。
1、創建有鉤子方法的父類
package templatemethod.pattern; //豆漿類,抽象類 public abstract class SoyaMilkWithHook { //這是模板方法,用final修飾,不允許子類覆蓋。模板方法定義了製作豆漿的程式 final void prepareRecipe(){ selectMaterial(); //判斷是否添加配料 if(customerWantsCondiments()){ addCondiments(); } soak(); beat(); } //選材方法,選擇黃豆 void selectMaterial(){ System.out.println("選擇好了新鮮黃豆"); } //可以添加不同的配料,在這裡設置為抽象方法,子類必須實現 abstract void addCondiments(); //浸泡 void soak(){ System.out.println("材料開始浸泡,大概需要5個小時"); } //放到豆漿機打碎 void beat(){ System.out.println("材料放到豆漿機打碎"); } //鉤子方法,是否添加配料 boolean customerWantsCondiments(){ return true; } }
2、創建純豆漿子類
package templatemethod.pattern; //製作純豆漿,不添加任何配料 public class PureSoyaMilk extends SoyaMilkWithHook{ @Override void addCondiments() { } //覆蓋鉤子方法,不添加配料 @Override boolean customerWantsCondiments(){ return false; } }
3、測試製作純豆漿
package templatemethod.pattern; public class PureSoyaMilkTest { public static void main(String[] args){ //製作純豆漿 System.out.println(); System.out.println("-----製作純豆漿步驟-------"); SoyaMilkWithHook pureSoyaMilk = new PureSoyaMilk(); pureSoyaMilk.prepareRecipe(); } }
測試結果:
鉤子方法的作用
1、讓子類實現演算法中的可選部分。演算法中的某些步驟是可選的,子類可以做出決定是否需要這些步驟。
2、如果鉤子對於子類的實現不重要時,子類可以對鉤子置之不理。
3、鉤子可以讓子類能夠有機會對模板方法中某些即將發生的(或剛剛發生的)步驟作出反應。可以在鉤子中實現我們對於某個步驟執行需要作出的動作,模板方法的某個步驟執行時,調用鉤子。
七、模板方法與策略的比較
1、相同點
(1)、都封裝了演算法。
2、不同點
(1)、策略使用組合方式實現演算法,客戶可以自由選擇演算法;模板方法使用繼承方式獲得演算法,確保演算法的基本結構不變。
(2)、策略代碼復用程度低,每個演算法都要對應一個類;模板方法代碼復用高,子類可以繼承父類的代碼。
(3)、策略依賴少,自己實現整個演算法;模板方法依賴高,子類依賴父類中的方法的實現。
八、使用場景
1、當要完成一件事情,它有固定的程式流程,但某些步驟是自定義的,使用模板方法。
2、當需要創建框架時,在超類中提供一個基礎的方法,達到代碼的復用,並允許子類指定行為。