【0】README
0.1)本文文字部分描述转自“head first设计模式”,旨在学习 迭代器模式 的基础知识;
【1】迭代器模式
1)定义:迭代器模式提供了一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示;
2)迭代器模式的作用: 迭代器模式让我们能游走于聚合内的每一个元素,而又不暴露其内部的表示;把游走的任务放在迭代器上,而不是聚合上。这样简化了聚合的接口和实现,也让责任各得其所;
【2】迭代器应用场景
2.1)problem:你知道吗? 开封菜要和北京烤鸭合并了(仅仅为了模拟其场景),并组建了新公司,这是个好消息,不过这里有一个问题?——假如我就是新公司的招待员,要求是,能应对顾客的需要打印定制菜单。但是,要知道开封菜的菜单用的是ArrayList存储,而北京烤鸭的菜单用的是数组存储。怎样打印用户的菜单呢?那就是遍历两种不同的菜单项,因为数组列表和数组的遍历方式不一样,所以要采用两次遍历,这似乎很麻烦。
2.2)我们用代码来说明(for downloading, please visit https://github.com/pacosonTang/HeadFirstDesignPattern/tree/master/iterator_composite_pattern/iterator_pattern/chapter9)
- step1)MenuItem(菜单项)
step2)开封菜的菜单public class MenuItem { private String name; private String desc; private double price; public MenuItem() {} public MenuItem(String name, String desc, double price) { this.name = name; this.desc = desc; this.price = price; } public String getName() { return name; } public String getDesc() { return desc; } public double getPrice() { return price; } @Override public String toString() { return "name=" + name + ", price=" + price; } }
step3)北京烤鸭的菜单public class KFCMenu { ArrayList<MenuItem> items; public KFCMenu() { items = new ArrayList<>(); addItem("hamburger", "hamburger", 2.00); addItem("salad", "salad", 3.00); addItem("french fries", "french fries", 4.00); addItem("chicken", "chicken", 6.00); } public void addItem(String name, String desc , double price) { MenuItem item = new MenuItem(name, desc, price); items.add(item); } public ArrayList getMenuItems() { return items; } //public Iterator<MenuItem> createIterator() { // return items.iterator(); //} }
step4)测试用例public class BJRoastDuckMenu { private final int MAX_ITEMS = 10; private int capacity = 0; MenuItem[] items; public BJRoastDuckMenu() { items = new MenuItem[MAX_ITEMS]; addItem("北京烤鸭", "北京烤鸭", 22.00); addItem("梅花扣肉", "salad", 23.00); addItem("蚂蚁上树", "french fries", 104.00); addItem("东坡肘子", "chicken", 236.00); } public void addItem(String name, String desc , double price) { MenuItem item = new MenuItem(name, desc, price); if(capacity == MAX_ITEMS) { System.out.println("there's no space to store new obj"); } else { items[capacity++] = item; } } public MenuItem[] getMenuItems() { return items; } public int getCapacity() { return capacity; } //public Iterator<MenuItem> createIterator() { // return new BJRoastDuckMenuIterator(items); //} }
打印结果)public class OldTraverseTest { public static void main(String[] args) { KFCMenu k_menu = new KFCMenu(); BJRoastDuckMenu bj_menu = new BJRoastDuckMenu(); ArrayList<MenuItem> list = k_menu.getMenuItems(); MenuItem[] items = bj_menu.getMenuItems(); System.out.println("=== 北京烤鸭的数组菜单 ==="); // 第一次遍历 北京烤鸭的数组菜单 for (int i = 0; i < bj_menu.getCapacity(); i++) { System.out.println(items[i]); } System.out.println("=== 开封菜的数组列表菜单 ==="); // 第二次遍历 开封菜的数组列表菜单 Iterator<MenuItem> iterator = list.iterator(); while(iterator.hasNext()) { System.out.println(iterator.next()); } } }
=== 北京烤鸭的数组菜单 === name=北京烤鸭, price=22.0 name=梅花扣肉, price=23.0 name=蚂蚁上树, price=104.0 name=东坡肘子, price=236.0 === 开封菜的数组列表菜单 === name=hamburger, price=2.0 name=salad, price=3.0 name=french fries, price=4.0 name=chicken, price=6.0
2.3)solution:交给迭代器模式来处理。迭代器模式需要依赖一个迭代器接口,一旦我们有了这个接口,就可以为各种对象集合实现迭代器:数组,列表,散列表等。
2.4)迭代器模式的遍历方法如下
- step0)创建 Menu接口,该接口有一个createIterator方法
public interface Menu { public Iterator<MenuItem> createIterator(); }
- step1)开封菜菜单实现 Menu接口,以添加 createIterator方法
step2)创建遍历北京烤鸭菜单的迭代器public class KFCMenu { ArrayList<MenuItem> items; public KFCMenu() { items = new ArrayList<>(); addItem("hamburger", "hamburger", 2.00); addItem("salad", "salad", 3.00); addItem("french fries", "french fries", 4.00); addItem("chicken", "chicken", 6.00); } public void addItem(String name, String desc , double price) { MenuItem item = new MenuItem(name, desc, price); items.add(item); } public ArrayList getMenuItems() { return items; } // 添加的创建迭代器的方法 public Iterator<MenuItem> createIterator() { return items.iterator(); } }
step3)北京烤鸭菜实现Menu接口,以添加 createIterator方法public class BJRoastDuckMenuIterator implements Iterator<MenuItem>{ private MenuItem[] items; private int position = 0; public BJRoastDuckMenuIterator(MenuItem[] items) { this.items = items; } @Override public boolean hasNext() { if(position >= items.length || items[position] == null) { return false; } else { return true; } } @Override public MenuItem next() { return items[position++]; } }
step4)创建服务员,封装遍历菜单的方法public class BJRoastDuckMenu implements Menu{ private final int MAX_ITEMS = 10; private int capacity = 0; MenuItem[] items; public BJRoastDuckMenu() { items = new MenuItem[MAX_ITEMS]; addItem("北京烤鸭", "北京烤鸭", 22.00); addItem("梅花扣肉", "salad", 23.00); addItem("蚂蚁上树", "french fries", 104.00); addItem("东坡肘子", "chicken", 236.00); } public void addItem(String name, String desc , double price) { MenuItem item = new MenuItem(name, desc, price); if(capacity == MAX_ITEMS) { System.out.println("there's no space to store new obj"); } else { items[capacity++] = item; } } public MenuItem[] getMenuItems() { return items; } public int getCapacity() { return capacity; } // 为北京烤鸭菜单创建迭代器 public Iterator<MenuItem> createIterator() { return new BJRoastDuckMenuIterator(items); } }
step5)测试用例public class Waitreee { private KFCMenu k_menu; private BJRoastDuckMenu bj_menu; public Waitreee(KFCMenu k_menu, BJRoastDuckMenu bj_menu) { this.k_menu = k_menu; this.bj_menu = bj_menu; } public void printMenu() { Iterator<MenuItem> k_iterator = k_menu.createIterator(); Iterator<MenuItem> bj_iterator = bj_menu.createIterator(); System.out.println("====== 开封菜的菜品菜单 ======"); printMenu(k_iterator); System.out.println("====== 北京烤鸭的菜品菜单 ======"); printMenu(bj_iterator); } private void printMenu(Iterator<MenuItem> iterator) { while(iterator.hasNext()) { System.out.println(iterator.next()); } } }
打印结果)public class MenuIteratorTest { public static void main(String[] args) { KFCMenu k_menu = new KFCMenu(); BJRoastDuckMenu bj_menu = new BJRoastDuckMenu(); Waitreee waitreee = new Waitreee(k_menu, bj_menu); waitreee.printMenu(); } }
====== 开封菜的菜品菜单 ====== name=hamburger, price=2.0 name=salad, price=3.0 name=french fries, price=4.0 name=chicken, price=6.0 ====== 北京烤鸭的菜品菜单 ====== name=北京烤鸭, price=22.0 name=梅花扣肉, price=23.0 name=蚂蚁上树, price=104.0 name=东坡肘子, price=236.0
【3】引入单一责任
3.1)定义:单一责任原则: 表示一个类应该只有一个引起变化的原因;
3.2)类的每个责任都有改变的潜在区域。超过一个责任,意味着找过改变的区域;这个原则告诉我们,尽量让每个类保持单一责任;
【4】对于迭代器模式的扩展
4.1)requirement:现在听说 某品牌咖啡店也要和 开封菜+北京烤鸭合并啦。我们看怎样来遍历它们的菜品
4.2)代码如下(你可以看到,我们只需要添加cafe菜单 和 修改 服务员的代码就可以了)
step1)cafe菜单step2)修改女服务员的遍历菜单的代码public class CafeMenu implements Menu{ Hashtable<String, MenuItem> items; public CafeMenu() { items = new Hashtable<>(); addItem("Caramel cafe", "Caramel cafe", 2.00); addItem("Mocha", "Mocha", 3.00); addItem("black rose", "black rose ", 4.00); addItem("irish iced coffee", "irish iced coffee", 6.00); } public void addItem(String name, String desc , double price) { MenuItem item = new MenuItem(name, desc, price); items.put(item.getName(), item); } @Override public Iterator<MenuItem> createIterator() { return items.values().iterator(); } }
step3)client 代码public class Waitreee { private KFCMenu k_menu; private BJRoastDuckMenu bj_menu; private CafeMenu cafe_menu; public Waitreee(KFCMenu k_menu, BJRoastDuckMenu bj_menu, CafeMenu cafe_menu) { this.k_menu = k_menu; this.bj_menu = bj_menu; this.cafe_menu = cafe_menu; } public Waitreee(KFCMenu k_menu, BJRoastDuckMenu bj_menu) { this.k_menu = k_menu; this.bj_menu = bj_menu; } public void printMenu() { Iterator<MenuItem> k_iterator = k_menu.createIterator(); Iterator<MenuItem> bj_iterator = bj_menu.createIterator(); Iterator<MenuItem> cafe_iterator = cafe_menu.createIterator();// this line System.out.println("====== 开封菜的菜品菜单 ======"); printMenu(k_iterator); System.out.println("====== 北京烤鸭的菜品菜单 ======"); printMenu(bj_iterator); System.out.println("====== 咖啡的菜品菜单 ======"); // this line printMenu(cafe_iterator); } private void printMenu(Iterator<MenuItem> iterator) { while(iterator.hasNext()) { System.out.println(iterator.next()); } } }
打印结果)public class MultipleMenuIteratorTest { public static void main(String[] args) { KFCMenu k_menu = new KFCMenu(); BJRoastDuckMenu bj_menu = new BJRoastDuckMenu(); CafeMenu cafe_menu = new CafeMenu(); Waitreee waitreee = new Waitreee(k_menu, bj_menu, cafe_menu); waitreee.printMenu(); } }
====== 开封菜的菜品菜单 ====== name=hamburger, price=2.0 name=salad, price=3.0 name=french fries, price=4.0 name=chicken, price=6.0 ====== 北京烤鸭的菜品菜单 ====== name=北京烤鸭, price=22.0 name=梅花扣肉, price=23.0 name=蚂蚁上树, price=104.0 name=东坡肘子, price=236.0 ====== 咖啡的菜品菜单 ====== name=irish iced coffee, price=6.0 name=Mocha, price=3.0 name=black rose, price=4.0 name=Caramel cafe, price=2.0