八、模板方法模式
定義
模板方法模式:在一個方法中定義一個算法骨架,而將一些步驟延遲到子類中。模板方法使得子類可以在不改變算法結構的情況下,重新定義算法中的某些步驟。
UML類圖
說明:
1、AbstractClass抽象中包含了模板方法,primitiveOpration1() 和primitiveOpration2() 是這個模板方法所用到的操作的抽象版本。
2、模板方法在實現算法的過程中,用到了兩個原語操作。模板方法本身和這兩個操作的具體實現之間被解耦了。
3、ConcreteClass 可以有多個具體類,每一個都實現了模板方法所需要的全部操作。ConcreteClass中的primitiveOpration1()和primitiveOpration2(),當模板方法需要這兩個抽象方法時,會調用他們。
簡單實現
AbstractMethod 抽象的模板方法定義
/*** 模板方法模式在一個方法中定義了一個算法骨架,而將一些步驟延伸到子類中。* 模板方法使得子類可以在不改變算法結構的情況下,重新定義算法中的某些步驟 */ public abstract class CaffeineBeverage {public final void prepareRecipe() {boilWater();brew();pourInCup();addCondiments();}//該方法在模板的模式中被稱作原語操作 具體子類必須實現它們protected abstract void addCondiments();protected abstract void brew();private final void pourInCup() {System.out.println("pour water in cup...");}private final void boilWater() {System.out.println("boil water...");} }ConcreteClass 抽象模板方法子類的實現
public class Tea extends CaffeineBeverage {@Overrideprotected void addCondiments() {System.out.println("Adding Lemon...");}@Overrideprotected void brew() {System.out.println("Adding Tea in Cup...");} } public class Coffee extends CaffeineBeverage {@Overrideprotected void addCondiments() {System.out.println("Adding Sugar and Milk...");}@Overrideprotected void brew() {System.out.println("Dripping Coffee through filter..."); } }測試類
public class BaristaTest {public static void main(String[] args) {Tea tea = new Tea();Coffee coffee = new Coffee();System.out.println("Making tea...");tea.prepareRecipe();System.out.println("Making coffee...");coffee.prepareRecipe();} }對模板方法進行掛鉤操作
CaffeineBeverageWithHook 帶鉤子的抽象模板方法定義
public abstract class CaffeineBeverageWithHook {public final void prepareRecipe() {boilWater();brew();pourInCup();//條件判斷語句,該條件是否成立,是有一個customerWantsCondiments()鉤子方法決定的。if (customerWantsCondiments()) {addCondiments();}}abstract void brew();abstract void addCondiments();private final void boilWater() {System.out.println("Boiling water");}private final void pourInCup() {System.out.println("Pouring into cup");}//這是一個鉤子,子類可以覆蓋這個方法,但是不見得一定這么做boolean customerWantsCondiments() {return true;} }使用鉤子 CoffeeWithHook 和 TeaWithHook
public class CoffeeWithHook extends CaffeineBeverageWithHook {public void brew() {System.out.println("Dripping Coffee through filter");}public void addCondiments() {System.out.println("Adding Sugar and Milk");}//鉤子的使用public boolean customerWantsCondiments() {String answer = getUserInput();if (answer.toLowerCase().startsWith("y")) {return true;} else {return false;}}private String getUserInput() {String answer = null;System.out.print("Would you like milk and sugar with your coffee (y/n)? ");BufferedReader in = new BufferedReader(new InputStreamReader(System.in));try {answer = in.readLine();} catch (IOException ioe) {System.err.println("IO error trying to read your answer");}if (answer == null) {return "no";}return answer;} } public class TeaWithHook extends CaffeineBeverageWithHook {public void brew() {System.out.println("Steeping the tea");}public void addCondiments() {System.out.println("Adding Lemon");}//鉤子的使用public boolean customerWantsCondiments() {String answer = getUserInput();if (answer.toLowerCase().startsWith("y")) {return true;} else {return false;}}private String getUserInput() {String answer = null;System.out.print("Would you like milk and sugar with your coffee (y/n)? ");BufferedReader in = new BufferedReader(new InputStreamReader(System.in));try {answer = in.readLine();} catch (IOException ioe) {System.err.println("IO error trying to read your answer");}if (answer == null) {return "no";}return answer;} }測試代碼
public class BeverageTestDrive {public static void main(String[] args) {TeaWithHook teaHook = new TeaWithHook();CoffeeWithHook coffeeHook = new CoffeeWithHook();System.out.println("\nMaking tea...");teaHook.prepareRecipe();System.out.println("\nMaking coffee...");coffeeHook.prepareRecipe();} }說明:鉤子是一種被聲明在抽象類中的方法,但是只有空的或者默認的實現。鉤子的存在,可以讓子類有能力對算法的不同點進行掛鉤。需不需要鉤子,由子類自行決定。
模板方法在Java排序中的實現
Duck類的排序
測試方法
public class DuckSortTestDrive {public static void main(String[] args) {Duck[] ducks = { new Duck("Daffy", 8), new Duck("Dewey", 2), new Duck("Howard", 7), new Duck("Louie", 2),new Duck("Donald", 10), new Duck("Huey", 2) };System.out.println("Before sorting:");display(ducks);Arrays.sort(ducks);System.out.println("\nAfter sorting:");display(ducks);}public static void display(Duck[] ducks) {for (int i = 0; i < ducks.length; i++) {System.out.println(ducks[i]);}} }說明:
1、數組的排序模板算法已經提供了算法,但是要讓這些模板方法知道如何比較duck,需要做的就是實現compareTo() 方法。
2、Array的設計者希望這個方法能夠用于所有的數組,所以把sort()變成靜態的方法,這樣一來,任何數組都可以使用這個方法。
3、因為sort()并不是真正定義在超類中,所以sort()方法需要知道已經實現了這個compareTo()方法,否則無法進行排序。
4、compareTo()方法返回是一個int類型的變量,返回值為 1、0、-1 三種。
5、實現Comparable接口,并重寫compareTo()方法。這種模板方法的實現,側重在于提供一個算法,并讓子類實現某些步驟,而數組的排序算法很明顯正是如此。
6、在Duck類的排序算法中,由于無法繼承Java數組,而sort()方法希望能夠用于所有的數組(每個數組都是不同的類)。所以Arrays提供了一個靜態方法,而由被排序對象內的每個元素自行提供比較大小的算法步驟。所以,這并不是標準的模板方法的實現,但是符合模板方法的模式精神。(由于不需要繼承數組就可以實現排序算法,使得排序變得更有彈性、更有用)
7、在Java API的實現中,java.io的InputStream類有一個read()方法,是由子類實現的,而這個方法又會被 read(byte b[], int off, int len) 模板方法使用。
模板方法模式與策略模式的比較
1、數組對象的排序,這部分和策略模式相似,都是使用對象的組合。但是在策略模式中,所組合的類實現了整個算法,數組所實現的排序算法并不完整,他需要一個類填補compareTo()方法的實現。因此更像模板方法。
2、策略模式并不是使用繼承來進行算法的實現,而是通過對象組合的方式,讓客戶可以選擇算法的實現。而模板方法模式是定義一個算法大綱,而由子類定義其中某些步驟的內容。這樣一來在算法的個別步驟可以有不同的細節實現,但算法的結構依然維持不變。
3、模板方法模式使用的重復代碼,被放入到超類中,好讓所有子類共享。而策略模式使用的是委托模型,使用對象組合,使得策略模式更加有彈性。利用策略模式,客戶可以在運行時改變自己的算法,而客戶所需要的,只是改用不同的策略對象罷了。
4、模板方法模式是經常被使用的模式,因為在超類中提供了一個基礎的方法,達到代碼的復用,并允許子類指定行為。這在創建框架的時候是非常有用的。
5、模板方法模式由于必須依賴超類中的方法的實現,算作算法的一部分。策略模式不需要依賴,整個算法自己去實現。所以依賴程度相對比較低。
要點
1、“模板方法”定義了算法的步驟,把這些步驟的實現延遲到子類。
2、模板方法為我們提供了一種代碼復用的技巧。
3、模板方法的抽象類可以定義具體的方法、抽象方法和鉤子。
4、抽象方法由子類實現。
5、鉤子是一種方法,它在抽象類中不做事,或者只做默認的事情,子類可以選擇要不要去覆蓋它。
6、為了防止子類改變模板方法中的算法,可以將模板方法聲明為final
7、將決策權放在高層模塊中,以便決定如何及何時調用低層模塊。
8、在真實實現的模板方法中,模板方法由很多的變體,不要指望一眼辨認出來。
9、策略模式和模板方法模式都封裝算法,一個用組合,一個用繼承。
10、工廠方法是模板方法的一種特殊版本。
轉載于:https://www.cnblogs.com/huacesun/p/6622491.html
總結
- 上一篇: jgGrid获得的id值是主键的id而不
- 下一篇: 用Redux来进行组件间通讯