designPattern

Design Pattern(8) - Iterator

以下文章是閱讀 深入淺出Design Pattern 還有 聖經還有Source making的筆記 圖片截圖自lynda.com的Foundations of Programming: Design Patterns 要更深入的理解一定要去看這兩本書

實作餐廳菜單

今天有一家餐廳A 想用Array來實做一個菜單

public class AMenu{
    static final int MAX_ITEMS = 6;
    int numberOfItems = 0;
    String[] menuItems;
    public AMenu() {
	menuItems = new String[MAX_ITEMS];
	addItem("Vegetarian BLT");
	addItem("BLT");
	addItem("Soup of the day");
	addItem("Hotdog");
	addItem("Steamed Veggies and Brown Rice");
	addItem("Pasta");
    }
  
    public void addItem(String name) 
    {
	if (numberOfItems >= MAX_ITEMS) {
	    System.err.println("Sorry, menu is full!  Can't add item to menu");
	} else {
	    menuItems[numberOfItems] = name;
	    numberOfItems = numberOfItems + 1;
	}
    }
 
    public String[] getMenuItems() {
	return menuItems;
    }
}

今天有個服務生到餐廳A打工 當他需要列出所有選項給客人的時候 他需要這麼做

AMenu amenu = new AMenu();
for(int i = 0; i < amenu.numberOfItems; i++){
    System.out.print(amenu.getMenuItems()[i]);
}

這個服務生很認真 他還同時在另一家餐廳B打工 餐廳B的菜單是用ArrayList實作

public class BMenu{
    ArrayList<String> menuItems;
 
    public BMenu() {
	menuItems = new ArrayList<String>();    
	addItem("K&B's Pancake Breakfast");
	addItem("Regular Pancake Breakfast");
	addItem("Blueberry Pancakes");
	addItem("Waffles");
    }

    public void addItem(String name)
    {
	menuItems.add(name);
    }
 
    public ArrayList<String> getMenuItems() {
	return menuItems;
    } 
}

當他需要列出所有選項給客人的時候

BMenu bmenu = new BMenu();
for(int i = 0; i < bmenu.getMenuItems().size(); i++){
    System.out.print(bmenu.getMenuItems().get(i));
}

嗯…兩個不同餐廳 因為實作方法的不同 client要iterate的用法也不同 挺麻煩的 而且餐廳不一定想讓服務生知道他們的菜單是怎麼實作的(想洩露商業機密)

哎呀 那不是很簡單嗎 把printMenu跟Menu class參在一起不就行了?

IMAGE ALT TEXT

每個餐廳各自實作自己的printMenu 這樣就不用洩漏實作的訊息給client

public class AMenu{
    //other method...
    public void printMenu(){
	for(int i = 0; i < amenu.numberOfItems; i++){
	    System.out.print(amenu.getMenuItems()[i]);
	}	
    }
}
public class BMenu{
    //other method...
    public void printMenu(){
	for(int i = 0; i < bmenu.getMenuItems().size(); i++){
	    System.out.print(bmenu.getMenuItems().get(i));
	}
    }
}

每個餐廳的menu 各自負責printMenu 這不是解決了嗎?

這時候就要導入一個設計模式中很重要的一個概念

Design Pattern rule #6: Single Responsibility Principle(一個class只負責一件事情)

就如同字面上的意思 每個class只負責做一件事 Menu這個class就只負責addItem 不要負責printMenu

所以我們為每一個菜單 都寫一個相對應的菜單Iterator

public class AMenuIterator{
    String[] items;
    int position = 0;
 
    public AMenuIterator(String[] items) {
	this.items = items;
    }
 
    public String next() {
	String menuItem = items[position];
	position = position + 1;
	return menuItem;
    }
 
    public boolean hasNext() {
	if (position >= items.length || items[position] == null) {
    	    return false;
	} else {
	    return true;
	}
    }
}
public class BMenuIterator {
    ArrayList<String> items;
    int position = 0;
 
    public PancakeHouseMenuIterator(ArrayList<String> items) {
	this.items = items;
    }
 
    public String next() {
	String menuItem = (String) items.get(position);
	position = position + 1;
	return menuItem;
    }
 
    public boolean hasNext() {
 	if (position >= items.size()) {
    	    return false;
	} else {
	    return true;
	}
    }
}

皆大歡喜 現在我printMenu的方法可以適用所有的Iterator 我不需要知道你這家餐廳是怎麼實作你的菜單 你只要給我你的Iterator 我就有辦法print

private static void printMenu(Iterator iterator) {
    while (iterator.hasNext()) {
        String menuItem = (String)iterator.next();
	System.out.println(menuItem);
    }
}	

反覆器模式

反覆器模式讓我們可以取得collection內的每一個元素 且不需要知道collection的實作

結構

Alt text

優缺點

1.Iterator跟Collection分離之後 每個Collection可以有很多的Iterate方式 比如一個Tree有preorder, postorder或inorder traversal 可以愛怎麼實作多少就實作多少 Collection本身毫無負擔

2.可以同時有很多個Iterator在跑 每個Iterator會負責記得跑到哪了

3.Collection的個數和Iterator的個數會成對增加 當你今天想用LinkedList來實作你的菜單 你就需要一個LinkedList的Iterator

願上蒼為你指引平坦的道路

願命運讓你遇見善良的人們

願遠方的陽光和璀璨的燈火

為你照亮每一片未來的天空

                                                             汪峰