Design Pattern(8) - Iterator
June 13, 2017以下文章是閱讀 深入淺出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參在一起不就行了?
每個餐廳各自實作自己的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的實作
結構
-
Iterator: 制定Iterator給外界存取的interface 定義遍歷元素的方法
-
ConcreteIterator: 實作Iterator 必須記錄現在存取到哪個元素了
-
Aggregate: 可以想成是任何的Collection 但必須有個函數return 這個Collection的Iterator
-
ConcreteAggregate: 實作Aggregate createIterator函數必須傳回相對應的ConcreateIterator
優缺點
1.Iterator跟Collection分離之後 每個Collection可以有很多的Iterate方式 比如一個Tree有preorder, postorder或inorder traversal 可以愛怎麼實作多少就實作多少 Collection本身毫無負擔
2.可以同時有很多個Iterator在跑 每個Iterator會負責記得跑到哪了
3.Collection的個數和Iterator的個數會成對增加 當你今天想用LinkedList來實作你的菜單 你就需要一個LinkedList的Iterator
願上蒼為你指引平坦的道路
願命運讓你遇見善良的人們
願遠方的陽光和璀璨的燈火
為你照亮每一片未來的天空
汪峰