2011年9月10日 星期六

[OO 設計模式] Composite 模式 : 樹狀結構的設計模式

前言 :
在 Iterator Design Pattern 我們使用了迭代模式來走訪 Menu 中的每一個 MenuItem, 雖然可行但只適用於一維的資料結構. 如果考慮更生活化的應用, 在一個總 Menu 中含有咖啡, 晚餐與點心的 Menu, 而每個 Menu 又有各自的 Menu 與 Menu Item, 也就是說這個 Menu 是類似於 Tree 的資料結構 (參考下圖), 這時你可以考慮使用 Composite Pattern.


Composite Pattern 介紹 :
考慮前面所述, 這時 Iterator 一維的資料走訪已經不敷使用, 取而代之的是我們將採用 Composite Pattern 來表示樹狀的資料結構 (參考下圖) :


而有關樹狀的結構以 node 來代表每一個在樹狀結構中的 component, 在這之前我們先來看看 Composite Pattern 的定義 : 

The Composite Pattern allows you to compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.

簡而言之就是透過 Composite Pattern 讓你可以將個別的 Component 組織成一個樹狀結構. 並且你在處理一個 Component 或者是一個樹狀結構的 Components 並沒有區別. 而所謂的 leaf node 就是沒有 children 的 component. 接著我們來看看 Composite Pattern 的 UML 類別圖 :


範例代碼 :
有了這些概念後, 接下來我們要來重新定義 UML 類別圖來解決樹形結構的 Menu 問題 :


相關代碼如下 :
- MenuComponent.java :
  1. package hf.dp.composite;  
  2.   
  3.   
  4. public abstract class MenuComponent {  
  5.     public void add(MenuComponent comp){throw new UnsupportedOperationException();}   
  6.     public void remove(MenuComponent comp){throw new UnsupportedOperationException();}    
  7.     public MenuComponent getChild(int i){throw new UnsupportedOperationException();}      
  8.     public String getName(){throw new UnsupportedOperationException();}  
  9.     public String getDescription(){throw new UnsupportedOperationException();}  
  10.     public double getPrice(){throw new UnsupportedOperationException();};  
  11.     public boolean isVegetarian(){throw new UnsupportedOperationException();}  
  12.     public void print(){throw new UnsupportedOperationException();};  
  13. }  

- MenuItem.java : (Leaf node)
  1. package hf.dp.composite;  
  2.   
  3. public class MenuItem extends MenuComponent{  
  4.     private String name;  
  5.     private String description;  
  6.     private boolean isVeget;  
  7.     private double price;  
  8.       
  9.     public MenuItem(String name, String des, boolean isVeget, double price)  
  10.     {  
  11.         this.name = name;  
  12.         this.description = des;  
  13.         this.isVeget = isVeget;  
  14.         this.price = price;  
  15.     }  
  16.   
  17.     @Override  
  18.     public String getName() {  
  19.         return name;  
  20.     }  
  21.   
  22.     @Override  
  23.     public String getDescription() {  
  24.         return description;  
  25.     }  
  26.   
  27.     @Override  
  28.     public boolean isVegetarian() {  
  29.         return isVeget;  
  30.     }  
  31.   
  32.     @Override  
  33.     public double getPrice() {  
  34.         return price;  
  35.     }  
  36.   
  37.     @Override  
  38.     public void print()  
  39.     {  
  40.         System.out.print("\t"+getName());  
  41.         if(isVegetarian())  
  42.         {  
  43.             System.out.print("(v)");              
  44.         }  
  45.         System.out.println(", Price="+getPrice());  
  46.         System.out.println("\t"+getDescription());  
  47.     }  
  48. }  

- Menu.java : (Composite Node)
  1. package hf.dp.composite;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Iterator;  
  5.   
  6. public class Menu extends MenuComponent{  
  7.     private String name;  
  8.     private String description;  
  9.     private ArrayList menuComponents;  
  10.       
  11.     public Menu(String name, String dest)  
  12.     {  
  13.         this.name = name;  
  14.         this.description = dest;  
  15.         menuComponents = new ArrayList();  
  16.     }  
  17.     @Override  
  18.     public String getName(){return name;}  
  19.     @Override  
  20.     public String getDescription(){return description;}  
  21.     @Override  
  22.     public void add(MenuComponent comp){menuComponents.add(comp);}  
  23.     @Override  
  24.     public void remove(MenuComponent comp){menuComponents.remove(comp);}  
  25.     @Override  
  26.     public MenuComponent getChild(int i){return menuComponents.get(i);}  
  27.     @Override  
  28.     public void print()  
  29.     {  
  30.         System.out.print("\n"+getName()+", "+getDescription()+"\n");  
  31.         System.out.println("=====================================");  
  32.   
  33.         for(MenuComponent comp:menuComponents)  
  34.         {  
  35.             comp.print();  
  36.         }  
  37.     }  
  38. }  

- MenuTestDrive.java : (測試 Main 類)
  1. package hf.dp.composite;  
  2.   
  3. public class MenuTestDrive {  
  4.     public static void main(String args[])  
  5.     {  
  6.         MenuComponent pancakeHouseMenu = new Menu("PANCAKE HOUSE MENU""Breakfast");  
  7.         MenuComponent dinerMenu = new Menu("DINER MENU","Lunch");  
  8.         MenuComponent cafeMenu = new Menu("CAFE MENU","Diner");  
  9.         MenuComponent dessertMenu = new Menu("DESSERT MENU","Dessert of course!");  
  10.           
  11.         MenuComponent allMenus = new Menu("ALL MENUS","All menus combination");  
  12.         allMenus.add(pancakeHouseMenu);  
  13.         allMenus.add(dinerMenu);  
  14.         allMenus.add(cafeMenu);  
  15.           
  16.         dinerMenu.add(new MenuItem("Pasta","Spaghetti with..."true3.89));  
  17.         dinerMenu.add(dessertMenu);  
  18.           
  19.         dessertMenu.add(new MenuItem("Apple Pie""Apple pie with a flakey crust, topped with..."true1.59));  
  20.           
  21.         Waitress waitress = new Waitress(allMenus);  
  22.         waitress.printMenu();  
  23.     }  
  24. }  

執行結果如下 :


補充說明 :
史蒂芬心得筆記 : Composite Pattern (合成模式)
[OO 設計模式] Gossip@DesignPattern : Structural - Composite 模式

沒有留言:

張貼留言

[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...