HeadFirst 設計模式 9迭代器與組合模式(餐廳合併)
迭代器模式
提供一種方法順序訪問一個聚合物件中的各個元素,而又不暴露其內部的表示。
迭代器模式讓我們能夠遊走於聚合內的每個元素,而又不暴露其內部的表示。把遊走的任務放在迭代器上,而不是聚合上。這樣簡化了聚合的介面和實現,也讓責任各得其所。
集合collection/聚合aggregate:指一群物件,其儲存方法可以是各式各樣的資料結構,例如:列表、陣列、散列表,無論用什麼方式儲存,一律可以視為是集合/聚合。
例項:
類圖public class MenuItem{ String name; String description; boolean vegetarian; double price; public MenuItem(String name,String description,boolean vegetarian,double price){ this.name=name; this.description=description; this.vegetarian=vegetarian; this.price=price; } public String getName(){ return name;} public String getDescription(){ return description;} public double getprice(){ return price;} public boolean isVegetarian(){ return vegetarian;} } public interface Iterator{ boolean hasNext(); //返回一個布林值,判斷是否還有更多的元素 Object next(); //返回下一個元素 } //實現迭代器介面 DinerMenu public class DinerMenuIterator implements Iterator{ MenuItem[] items; int position = 0; //position記錄當前陣列遍歷的位置 public DinerMenuIterator(MenuItem[] items){ //構造器需要被傳入一個選單項的陣列當作引數 this.items=items; } public Object next(){ //返回陣列內的下一項,並遞增其位置 MenuItem menuItem= items[position]; position=position+1; return menuItem; } public boolean hasNext(){ /*檢查是否已經取得陣列內所有的元素,如果還有元素待遍歷則返回true; 由於使用的是固定長度的陣列,所以不但要檢查是否超出了陣列長度,也必須檢查是否下一項是null,如果是null,就沒有其他項了 */ if(position>=items.length||items[position]==null){ return false; }else{ return true; } } } //實現迭代器介面 PancakeHouseMenu public class PancakeHouseMenuIterator implements Iterator{ ArrayList items; int position = 0; //position記錄當前陣列遍歷的位置 public PancakeHouseMenuIterator(ArrayList items){ //構造器需要被傳入一個選單項的陣列當作引數 this.items=items; } public Object next(){ //返回陣列內的下一項,並遞增其位置 MenuItem menuItem= items.get(position); position=position+1; return menuItem; } public boolean hasNext(){ if(position>=items.size()||items.get(position)==null){ return false; }else{ return true; } } } public class DinerMenu{ static final int MAX_ITEMS=6; int numberOfItems=0; MenuItem[] menuItems; public DinerMenu(){ menuItems=new MenuItem[MAX_ITEMS]; addItem("Vegetarian BLT","1111111111111",true,2.99); addItem("BLT","222222222222",false,2.99); addItem("Soup of the day","2222222222222",false,3.29); addItem("Hotdog","33333333333333",false,3.05); //繼續加入其他項 } public void addItem(String name,String description,boolean vegetarian,double price){ MenuItem menuItem= new MenuItem(name,description,vegetarian,price); if(numberOfItems>=MAX_ITEMS){ System.err.println("Sorry,menu is full! Can't add item to menu"); }else{ menuItems[numberOfItems]=menuItem; numberOfItems=numberOfItems+1; } } //返回迭代器介面。客戶不需要知道餐廳選單是如何維護選單項的,也不需要知道迭代器是如何實現的,客戶只需要直接使用這個迭代器遍歷選單項即可。 public Iteraor createIterator(){ return new DinerMenuIterator(menuItems); } //選單的其他方法。。。。 } public class PancakeHouseMenu{ ArrayList menuItems; public PancakeHouseMenu(){ menuItems=new ArrayList(); addItem("K&B's Pancake Breakfast","1111111111111",true,2.99); addItem("Regular Pancake Breakfast","222222222222",false,2.99); addItem("Blueberry Pancakes","2222222222222",true,3.29); addItem("Waffles","33333333333333",true,3.05); } public void addItem(String name,String description,boolean vegetarian,double price){ MenuItem menuItem= new MenuItem(name,description,vegetarian,price); menuItems.add(menuItem); } public Iteraor createIterator(){ return new PancakeHouseMenuIterator(menuItems); } //其他方法 } public class Waitress{ PancakeHouseMenu pancakeHouseMenu; DinerMenu dinerMenu; //在構造器中,女招待照顧兩個選單 public Waitress(PancakeHouseMenu pancakeHouseMenu,DinerMenu dinerMenu){ this.pancakeHouseMenu=pancakeHouseMenu; this.dinerMenu=dinerMenu; } public void printMenu(){ Iterator pancakeIterator=pancakeHouseMenu.createIterator(); Iterator dinerIterator=dinerMenu.createIterator(); System.out.println("Menu\n-------------\nBREAKFAST"); printMenu(pancakeIterator); System.out.println("\nLUNCH"); printMenu(dinerIterator); } private void printMenu(Iterator iterator){ //過載printMenu方法,使用迭代器遍歷選單項並列印 while(iterator.hasNext()){ MenuItem menuItem=(MenuItem)iterator.next(); System.out.print(menuItem.getName()+","); System.out.print(menuItem.getPrice()+"--"); System.out.println(menuItem.getDescription()); } } //其他方法 } public class MenuTestDriver{ public static void main(String args[]){ PancakeHouseMenu pancakeHouseMenu=new PancakeHouseMenu(); DinerMenu dinerMenu= new DinerMenu(); Waitress waitress=new Waitress(pancakeHouseMenu,dinerMenu); waitress.printMenu(); } }
進一步改進:為兩份選單設立共同的介面。
設計原則
一個類應該只有一個引起變化的原因。
類的每個責任都有改變的潛在區域。超過一個責任則意味著多個改變的區域。該原則說明了應該儘量讓每個類保持單一責任。
組合模式
允許將物件組合成樹形結構來表現“整體/部分”層次結構。組合能夠讓客戶以抑制的方式處理個別對象以及物件組合。
組合模式讓我們 能用樹形方式建立物件的結構,樹裡面包含了組合以及個別的物件。使用組合結構,我們能把相同的操作應用在組合和個別物件上。即:在大多數情況下,我們可以忽略物件組合和個別物件的差別。
區別各類模式:
策略模式:封裝可互換的行為,並使用委託決定使用哪一個。
介面卡模式:改變一個或多個類的介面。
迭代器模式:提供一個方式來遍歷集合,而無需暴露集合的實現。
外觀模式:簡化一群類的介面。
組合模式:客戶可以將物件的集合以及個別的物件一視同仁。
觀察者模式:當某個狀態改變是,允許一群物件能被通知到。
要點:
(1)迭代器允許訪問聚合的元素,而不需暴露他的內部結構。
(2)迭代器將遍歷聚合的工作封裝進一個物件中。
(3)當使用迭代器的時候,我們依賴聚合提供遍歷。
(4)迭代器提供一個通用的介面,讓我們遍歷聚合的項,當我們編碼使用聚合的項時,就可以使用多型機制。
(5)我們應該努力讓一個類只分配一個責任。
(6)組合模式提供一個結構,可同時包容個別物件和組合物件。
(7)組合模式允許客戶對個別物件和組合物件一視同仁。
(8)組合結構內的任意物件稱為元件,元件可以是組合,也可以是葉節點。
(9)在實現組合模式時,有許多設計上的折中。要根據需要平衡透明性和安全性。