2010年7月30日 星期五

[OO 設計模式] the Template Method Pattern : 封裝演算法


前言 :
這裡將會以一個簡單例子來說明 Template Method Pattern 的使用. 考慮假設咖啡與茶的泡製過程如下 :
* 咖啡 :
- Boiling water
- Brewing Coffee
- Pure Coffee into cup
- Adding condiments

* 茶 :
- Boiling water
- Steeping Tea
- Pure Tea into cup
- Adding condiments

這裡我們把 咖啡 與 茶 的製作過程當作是演算法的過程. 因此我們打算使用一個介面 (ICaffeineBeverage.java) 來定義該演算法的呼叫過程 :
- ICaffeineBeverage.java 代碼 :
  1. package hf.dp.ch08.proto;  
  2.   
  3. public interface ICaffeineBeverage {  
  4.     public void prepareReceipe();  
  5.     public void brew();  
  6.     public void addCondiments();  
  7. }  

接著發現咖啡與茶的泡製過程有兩個步驟是重複的, 所以我們使用了一個抽像類別 (AbstractCaffeineBeverage.java) 來把相同的演算法實現, 以避免在接著子類別實現發生重複代碼 :
- AbstractCaffeineBeverage.java 代碼 :
  1. package hf.dp.ch08.proto;  
  2.   
  3. public abstract class AbstractCaffeineBeverage implements ICaffeineBeverage{  
  4.     public final void prepareReceipe(){  
  5.         boilWater();  
  6.         brew();  
  7.         pureInCup();  
  8.         addCondiments();  
  9.     }  
  10.     // 重複演算法 (1)  
  11.     public void pureInCup(){System.out.println("Pure into Cup");}  
  12.     // 重複演算法 (2)  
  13.     public void boilWater(){System.out.println("Boiling water");}  
  14. }  

接著我們分別實現類別 Tea 與 Coffee, 以實現個別獨立的演算法過程 :
- Coffee.java 代碼 :
  1. package hf.dp.ch08.Coffee;  
  2.   
  3. import hf.dp.ch08.proto.AbstractCaffeineBeverage;  
  4.   
  5. public class Coffee extends AbstractCaffeineBeverage{  
  6.   
  7.     @Override  
  8.     public void addCondiments() {  
  9.         System.out.println("Adding Sugar and Milk");  
  10.     }  
  11.   
  12.     @Override  
  13.     public void brew() {  
  14.         System.out.println("Dripping Coffee through filter");                 
  15.     }  
  16. }  

- Tea.java 代碼 :
  1. package hf.dp.ch08.Tea;  
  2.   
  3. import hf.dp.ch08.proto.AbstractCaffeineBeverage;  
  4.   
  5. public class Tea extends AbstractCaffeineBeverage{  
  6.   
  7.     @Override  
  8.     public void addCondiments() {  
  9.         System.out.println("Adding Lemon");  
  10.     }  
  11.   
  12.     @Override  
  13.     public void brew() {  
  14.         System.out.println("Steeping the Tea");  
  15.     }  
  16. }  

接著問題來了, 我們希望能夠讓物件使用者能夠自由決定 Condiments 是否進行添加. 因此我們採用 Hooking 的概念讓在進行呼叫函數:addCondiments() 讓使用者由Console 輸入 yes/no 來決定是否添加 Condiments. 因此我們重寫抽像類別, 因為函數:prepareReceipe() 不管如何都會呼叫 函數:addCondiments(), 因此我們又定義了一個函數:customerWantCondiment() 讓使用者來決定是否添加 Condiments. 而在抽像類別中我們預設函數:customerWantCondiment() 返回 true, 再讓子類別繼承後客製化想要的結果 :
- CaffeineBeverageWithHook.java 代碼 :
  1. package hf.dp.ch08.proto;  
  2.   
  3. public abstract class CaffeineBeverageWithHook implements ICaffeineBeverage{  
  4.     public final void prepareReceipe(){  
  5.         boilWater();  
  6.         brew();  
  7.         pureInCup();  
  8.         if(customerWantCondiment()) {  
  9.             addCondiments();  
  10.         }  
  11.     }   
  12.       
  13.     public void pureInCup(){System.out.println("Pure into Cup");}  
  14.     public void boilWater(){System.out.println("Boiling water");}  
  15.       
  16.     protected boolean customerWantCondiment(){  
  17.         return true;  
  18.     }  
  19. }  
- CoffeeWithHook.java 代碼 :
  1. package hf.dp.ch08.Coffee;  
  2.   
  3. import java.io.BufferedReader;  
  4. import java.io.IOException;  
  5. import java.io.InputStream;  
  6. import java.io.InputStreamReader;  
  7.   
  8. import hf.dp.ch08.proto.CaffeineBeverageWithHook;  
  9.   
  10. public class CoffeeWithHook extends CaffeineBeverageWithHook{  
  11.   
  12.     @Override  
  13.     public void addCondiments() {  
  14.         System.out.println("Adding Sugar and Milk");          
  15.     }  
  16.   
  17.     @Override  
  18.     public void brew() {  
  19.         System.out.println("Dripping Coffee through filter");         
  20.     }  
  21.       
  22.     protected boolean customerWantCondiment(){  
  23.         if(getUserInput().startsWith("y")) {  
  24.             return true;  
  25.         }  
  26.         return false;  
  27.     }  
  28.   
  29.     protected String getUserInput() {  
  30.         String answer = null;  
  31.         System.out.println("Do you want condiment?(yes or no): ");  
  32.         try{  
  33.             BufferedReader br = new BufferedReader(new InputStreamReader(System.in));  
  34.             answer = br.readLine();  
  35.             if(answer == nullreturn "no";  
  36.             return answer;  
  37.         }catch(IOException ioe) {  
  38.             ioe.printStackTrace();  
  39.         }  
  40.         return "no";  
  41.     }  
  42. }  

接著使用一個 Test drive 類別來驗證我們的設計 :
- BeverageTestDrive.java 代碼 :
  1. package hf.dp.ch08;  
  2.   
  3. import hf.dp.ch08.Coffee.CoffeeWithHook;  
  4. import hf.dp.ch08.proto.ICaffeineBeverage;  
  5.   
  6. public class BeverageTestDrive {  
  7.     public static void main(String args[]) {  
  8.         ICaffeineBeverage beverage = new CoffeeWithHook();  
  9.         beverage.prepareReceipe();  
  10.     }  
  11. }  

執行結果 :
Boiling water
Dripping Coffee through filter
Pure into Cup
Do you want condiment?(yes or no):
yes <輸入yes 表示添加 Condiment>
Adding Sugar and Milk

* Template Method Pattern 介紹 :
接著我們來介紹這邊使用的 Pattern. Template Pattern 定義了演算法的呼叫過程, 並允許子類別重新定義某個演算法或多個演算法的實現過程. 這裡我們先來看定義 :
- Template Pattern 定義 :
The Template Method Pattern defines the skeleton of an algorithm in a method, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.

- UML 圖 :

其實在Java 的 JDK 也有使用到這個模式, 想想看在進行 Array 的 Sorting 時, 則 Array 的元素必序實現介面 IComparable, 事實上就是在定義 Sorting 時的 Algorithm, 考慮範例代碼如下 :
- Duck.java 代碼 :
  1. package hf.dp.ch08.duck;  
  2.   
  3. public class Duck implements Comparable{  
  4.     String name;  
  5.     int weight;  
  6.       
  7.     public Duck(String n, int w) {name=n;weight=w;}  
  8.   
  9.     public String toString(){return name+" weights "+weight;};  
  10.       
  11.     @Override  
  12.     public int compareTo(Object o) {  
  13.         Duck otherDuck = (Duck)o;  
  14.         if(this.weightreturn -1;  
  15.         else if(this.weight==otherDuck.weight) return 0;  
  16.         else return 1;  
  17.     }  
  18.   
  19. }  
- DuckSortTestDrive.java 代碼 :
  1. package hf.dp.ch08;  
  2.   
  3. import java.util.Arrays;  
  4. import hf.dp.ch08.duck.Duck;  
  5.   
  6. public class DuckSortTestDrive {  
  7.   
  8.     public static void main(String[] args) {  
  9.         Duck[] ducks = {new Duck("ab"10),  
  10.                         new Duck("Helen"21),  
  11.                         new Duck("John"13),  
  12.                         new Duck("Peter"19),  
  13.                         new Duck("Ken"18),  
  14.                         new Duck("Heny"15)};  
  15.         System.out.println("\t>>>Before sorting: ");  
  16.         display(ducks);  
  17.         System.out.println("\t>>>After sorting: ");  
  18.         Arrays.sort(ducks);  
  19.         display(ducks);  
  20.     }  
  21.   
  22.     public static void display(Duck[] ds) {  
  23.         for(int i=0; i
  24.             System.out.println(ds[i]);  
  25.         }  
  26.     }  
  27. }  

執行結果 :
>>>Before sorting:
ab weights 10
Helen weights 21
John weights 13
Peter weights 19
Ken weights 18
Heny weights 15
>>>After sorting:
ab weights 10
John weights 13
Heny weights 15
Ken weights 18
Peter weights 19
Helen weights 21

補充說明 :
Wiki : Template method pattern
A template method defines the program skeleton of an algorithm. One or more of the algorithm steps are able to be overridden by subclasses to provide their own concrete implementation. This allows differing behaviors while ensuring that the overarching algorithm is still followed.
This message was edited 8 times. Last update was at 16/07/2010 22:59:10

沒有留言:

張貼留言

[Git 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...