1. 程式人生 > >HeadFirst 設計模式 9迭代器與組合模式(餐廳合併)

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)在實現組合模式時,有許多設計上的折中。要根據需要平衡透明性和安全性。