让客户遍历我们的数组、堆栈、列表或者散列表时,无法知道我们存储对象的方式,就靠今天的迭代器模式了。

案例

我们有两个餐饮店要合并经营,他们的菜品有共同的地方,所以我们这两个商店类需要一个共同的菜单属性,我们单独创建一个菜单类,如下。

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 boolean isVegetarian() {return vegetarian;}public double getPrice() {return price;}
}

现在来看两个商店类。

public class DinerMenu {static final int MAX_ITEMS = 6;int numberOfItems = 0;MenuItem[] menuItems;public DinerMenu() {menuItems = new MenuItem[MAX_ITEMS];addItem("Vegetarian BLT","(Fakin') Bacon with lettuce & tomato on whole wheat",true,2.99);addItem("BLT","Bacon with lettuce & tomato on whole wheat",false,2.99);addItem("Soup of the day","Soup of the day, with a side of potato salad",false,3.29);addItem("Hotdog","A hot dog, with saurkraut, relish, onions, toped with cheese",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 += 1;}}public MenuItem[] getMenuItems() {return menuItems;}
}public class PancakeHouseMenu {ArrayList menuItems;public PancakeHouseMenu() {menuItems = new ArrayList();addItem("K&B's Pancake Breakfast","Pancakes with scrambled eggs, and toast",true,2.99);addItem("Regular Pancake Breakfast","Pancakes with fried eggs, sausage",false,2.99);addItem("Blueberry Pancakes","Pancakes made with fresh blueberries",true,3.49);addItem("Waffles","Waffles, with your choice of blueberries or strawberries",true,3.59);}public void addItem(String name, String description, boolean vegetarian, double price) {MenuItem menuItem = new MenuItem(name, description, vegetarian, price);menuItems.add(menuItem);}public ArrayList getMenuItems() {return menuItems;}
}

结构是类似的,都有一个菜单属性,不过一个是集合,一个是数组,在初始化的时候添加几样菜品,还有一个返回菜单的 getter() 方法。

接下来是我们要操作的重头戏,女侍者类,里面有打印菜单的方法。

public class Waitress {public void printMenu() {PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();DinerMenu dinerMenu = new DinerMenu();ArrayList houseMenuMenuItems = pancakeHouseMenu.getMenuItems();MenuItem[] dinerMenuMenuItems = dinerMenu.getMenuItems();// 循环遍历for (int i = 0; i < houseMenuMenuItems.size(); i++) {MenuItem menuItem = (MenuItem) houseMenuMenuItems.get(i);System.out.println(menuItem.getName() + " ");System.out.println(menuItem.getPrice() + " ");System.out.println(menuItem.getDescription());}for (int i = 0; i < dinerMenuMenuItems.length; i++) {MenuItem dinerMenuMenuItem = dinerMenuMenuItems[i];System.out.println(dinerMenuMenuItem.getName() + " ");System.out.println(dinerMenuMenuItem.getPrice() + " ");System.out.println(dinerMenuMenuItem.getDescription());}}
}

这是最简单的使用 for 循环遍历,假如我们又有新的店家要联合开店,就要在这个方法中继续增加 for 循环遍历,很重复很冗余,我们应该封装变化的部分,我们来就使用迭代器模式来改进。首先创建一个迭代器接口。

public interface Iterator {boolean hasNext();Object next();
}

然后分别创建集合与数组的迭代器类,他们实现上面的方法,一个是判断是否仍有下一个元素 hasNext() 方法,一个是返回下一个元素的 next() 方法。

public class DinerMenuIterator implements Iterator {MenuItem[] items;int position = 0;public DinerMenuIterator(MenuItem[] items) {this.items = items;}@Overridepublic boolean hasNext() {if (position >= items.length || items[position] == null) {return false;} else {return true;}}@Overridepublic Object next() {MenuItem menuItem = items[position];position += 1;return menuItem;}
}public class PancakeHouseMenuIterator implements Iterator {ArrayList items;int position = 0;public PancakeHouseMenuIterator(ArrayList items) {this.items = items;}@Overridepublic boolean hasNext() {if (position >= items.size() || items.get(position) == null) {return false;} else {return true;}}@Overridepublic Object next() {MenuItem menuItem = (MenuItem)items.get(position);position += 1;return menuItem;}
}

然后修改两个菜单类,删除掉 getMenuItems() 方法,因为这会暴露我们代码内部的实现,然后添加一个返回迭代器的方法,我们通过这个迭代器进行数据的遍历。

public class DinerMenu {static final int MAX_ITEMS = 6;int numberOfItems = 0;MenuItem[] menuItems;public DinerMenu() {menuItems = new MenuItem[MAX_ITEMS];addItem("Vegetarian BLT","(Fakin') Bacon with lettuce & tomato on whole wheat",true,2.99);addItem("BLT","Bacon with lettuce & tomato on whole wheat",false,2.99);addItem("Soup of the day","Soup of the day, with a side of potato salad",false,3.29);addItem("Hotdog","A hot dog, with saurkraut, relish, onions, toped with cheese",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 += 1;}}public Iterator createIterator() {return new DinerMenuIterator(menuItems);}
}public class PancakeHouseMenu {ArrayList menuItems;public PancakeHouseMenu() {menuItems = new ArrayList();addItem("K&B's Pancake Breakfast","Pancakes with scrambled eggs, and toast",true,2.99);addItem("Regular Pancake Breakfast","Pancakes with fried eggs, sausage",false,2.99);addItem("Blueberry Pancakes","Pancakes made with fresh blueberries",true,3.49);addItem("Waffles","Waffles, with your choice of blueberries or strawberries",true,3.59);}public void addItem(String name, String description, boolean vegetarian, double price) {MenuItem menuItem = new MenuItem(name, description, vegetarian, price);menuItems.add(menuItem);}public Iterator creatorIterator() {return new PancakeHouseMenuIterator(menuItems);}
}

最后就是我们的女侍者类了,创建一个私有方法,用以利用迭代器对数据进行遍历,再修改打印菜单方法,利用刚刚的 creatorIterator() 方法返回迭代器,配合私有方法进行遍历数据。

public class Waitress {public void printMenu() {DinerMenu dinerMenu = new DinerMenu();PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();Iterator dinerIterator = dinerMenu.createIterator();Iterator pancakeIterator = pancakeHouseMenu.creatorIterator();System.out.println("MENU\n----\nBREAKFAST");printMenu(dinerIterator);System.out.println("\nLUNCH");printMenu(pancakeIterator);}private void printMenu(Iterator iterator) {while (iterator.hasNext()) {MenuItem menuItem = (MenuItem)iterator.next();System.out.println(menuItem.getName() + " ");System.out.println(menuItem.getPrice() + " -- ");System.out.println(menuItem.getDescription());}}
}

我们不再需要 getMenuItem() 方法,删掉,取而代之是获取迭代器对象,然后使用下面的 printMenu(Iterator iterator) 方法。

这样我们只需要给他们两个迭代器, 然后各自商店增加一个返回迭代器对象的方法就可以了。这样我们的女侍者很容易扩展和维护,当新增商店时,只需要创建这个商店对象,返回它的迭代器,调用 printMenu(Iterator iterator) 方法。

这个迭代器让我们把女侍者从具体类的实现中解耦,她不需要知道菜单使用的是什么数据结构,只要知道她可以拿到迭代器即可。迭代器使我们可以遍历聚合中的每个元素,而不是必须在方法内实现遍历,只是让我们能在数据聚合中访问到每个元素,我们封装了遍历。

我们继续改进我们的设计,事实上,ArrayList 已经为我们提供了一个获取 Iterator 的方法,所以我们修改一下 PancakeHouseMenu 的 createIterator() 方法,先导入 java.util.Iterator,我们使用 Java 提供的迭代器接口。

public Iterator creatorIterator() {return menuItems.iterator();}

下面我们修改 DinerMenu,使其满足 java.util.Iterator 接口的需求,这需要从 DinerMenuIterator 开始修改。导入 java.util.Iterator,实现 remove() 方法。

public class DinerMenuIterator implements Iterator {MenuItem[] items;int position = 0;public DinerMenuIterator(MenuItem[] items) {this.items = items;}@Overridepublic boolean hasNext() {if (position >= items.length || items[position] == null) {return false;} else {return true;}}@Overridepublic Object next() {MenuItem menuItem = items[position];position += 1;return menuItem;}@Overridepublic void remove() {if (position <= 0) {throw new IllegalStateException("You can't remove an item until you've done at least one next()");}if (items[position - 1] != null) {for (int i = position - 1; i < items.length - 1; i++) {items[i] = items[i+1];}items[items.length-1] = null;}}
}

然后在 DinerMenu 中添加导入 java.util.Iterator。我们还可以通过添加 Menu 接口,令两个餐厅菜单类实现这个接口,方便我们在侍女类中的方法调用,这样我们是面向接口编程,降低具体实现和侍女类的耦合。

public interface Menu {public Iterator createIterator();
}public class DinerMenu implements Menu {...}public class PancakeHouseMenu implements Menu {...}

实现这个接口后,我们再看侍女类。

public class Waitress {public void printMenu() {Menu dinerMenu = new DinerMenu();Menu pancakeHouseMenu = new PancakeHouseMenu();Iterator dinerIterator = dinerMenu.createIterator();Iterator pancakeIterator = pancakeHouseMenu.createIterator();System.out.println("MENU\n----\nBREAKFAST");printMenu(dinerIterator);System.out.println("\nLUNCH");printMenu(pancakeIterator);}private void printMenu(Iterator iterator) {while (iterator.hasNext()) {MenuItem menuItem = (MenuItem)iterator.next();System.out.println(menuItem.getName() + " ");System.out.println(menuItem.getPrice() + " -- ");System.out.println(menuItem.getDescription());}}
}

导入 java.util.Iterator,使用 Menu 声明两个菜单类,而不具体类,「针对接口编程,而不是针对实现编程」。我们就可以减少女招待和具体类之间的依赖。

迭代器模式定义

迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

迭代器模式让我们可以遍历数据集内的每一个元素,且又不暴露其的数据结构,把元素遍历的责任交给迭代器,而不是聚合对象,这让聚合的接口和实现变得更简洁,也可以让聚合更专注于它所应完成的任务,而不用理会遍历数据的事情。

「单一责任」设计原则:一个类应该只有一个引起变化的原因。

应该避免类的改变,因为修改代码很容易造成许多潜在的错误。如果有一个类具有两个改变的原因,那么这个类改变的几率就会上升,而当它真正有改变的时候,这两方面都会受到影响,所以我们就按照这个原则,只将一个责任指派给一个类。

我们要学会的就是区分设计中的责任,这是很困难的,看着一大群的行为,然后将它们集中在一起,尽管他们可能属于两个或多个不同的责任,但我们努力不懈地检查自己的设计,随着系统的成长,随时观察我们的类改变的原因是否存在超过一个。

新增咖啡厅

现在我们要新增一个咖啡厅到我们的系统,那么该如何设计呢?

public class CafeMenu implements Menu {Hashtable menuItems = new Hashtable();public CafeMenu() {addItem("Veggie Burger and Air Fries","Veggie burger on a whole wheat bun, lettuce, tomato, and fries",true,3.99);addItem("Soup of the day","A cup of the soup of the day, with a side salad",false,3.69);addItem("Burrito","A large burrito, with whole pinto beans, salsa, guacamole",true,4.29);}public void addItem(String name, String description, boolean vegetarian, double price) {MenuItem menuItem = new MenuItem(name, description, vegetarian, price);menuItems.put(menuItem.getName(), menuItem);}@Overridepublic Iterator createIterator() {return menuItems.values().iterator();}
}

这是咖啡菜单类,和前面两个菜单类很类似,都是实现菜单接口,返回迭代器。然后再在女侍者类的 printMenu() 方法添加几行,创建咖啡菜单类,获取迭代器,调用 printMenu(Iterator iterator) 方法遍历数据。

public void printMenu() {Menu dinerMenu = new DinerMenu();Menu pancakeHouseMenu = new PancakeHouseMenu();CafeMenu cafeMenu = new CafeMenu();Iterator dinerIterator = dinerMenu.createIterator();Iterator pancakeIterator = pancakeHouseMenu.createIterator();Iterator cafeMenuIterator = cafeMenu.createIterator();System.out.println("MENU\n----\nBREAKFAST");printMenu(dinerIterator);System.out.println("\nLUNCH");printMenu(pancakeIterator);System.out.println("\nDINNER");printMenu(cafeMenuIterator);}

这样即可完成添加新的菜单的目的了,但是还有一个问题,程序中多次调用 printMenu(),实在是不美观,而且每次新增加菜单,还要打开女侍者类新增更多代码,我们如何修改这个设计呢?来看代码:

public class Waitress {ArrayList menus;public Waitress(ArrayList menus) {this.menus = menus;}public void printMenu() {Iterator iterator = menus.iterator();while (iterator.hasNext()) {Menu next = (Menu) iterator.next();printMenu(next.createIterator());}}private void printMenu(Iterator iterator) {while (iterator.hasNext()) {MenuItem menuItem = (MenuItem)iterator.next();System.out.println(menuItem.getName() + " ");System.out.println(menuItem.getPrice() + " -- ");System.out.println(menuItem.getDescription());}}
}

做一个菜单类集合属性,当我们调试的时候只需要给女侍者对象传入一个菜单集合,然后再在 printMenu() 方法中遍历这个集合就可以让我们代码更简便。

现在他们希望在后面加上一份餐后甜点的「子菜单」。有点类似于一棵树的结构,我们不能把甜点菜单赋值给菜单项数组,所以又要修改了!修改见下篇博客。

《Head First 设计模式》读书笔记——迭代器模式相关推荐

  1. 设计模式读书笔记-----模板方法模式

    首先我们先来看两个例子:冲咖啡和泡茶.冲咖啡和泡茶的基本流程如下: 所以用代码来创建如下: 咖啡:Caffee.java public class Coffee {void prepareRecipe ...

  2. 设计模式读书笔记-单件模式

    单件模式- 确保一个类只有一个实例,全局只有一个入口点. 类如下: public class Singleton { private static Singleton uniqueInstance; ...

  3. 设计模式读书笔记-----代理模式

    在我们实际生活中代理情况无处不在!你在淘宝上面买东西,你使用支付宝平台支付,卖家请物流公司发货.你请朋友帮你拿包裹,在这个过程汇总支付宝.物流公司.你朋友都扮演者"第三者"的角色在 ...

  4. Head First设计模式读书笔记——策略模式

    问题描述: 目前的任务是实现一个FPS类游戏的各种角色(友军.敌军.平民和狗.猫.鸭子等动物)以及他们的各种行为(攻击.游泳等). 设计方案一 很简单,只要实现一个角色超类,将角色的各种行为放入超类中 ...

  5. 设计模式读书笔记-----备忘录模式

    个人比较喜欢玩单机游戏,什么仙剑.古剑.鬼泣.使命召唤.三国无双等等一系列的游戏我都玩过(现在期待凡人修仙传),对于这些游戏除了剧情好.场面大.爽快之外,还可以随时存档,等到下次想玩了又可以从刚开始的 ...

  6. 设计模式学习笔记——迭代器(Iterator)模式

    设计模式学习笔记--迭代器(Iterator)模式 @(设计模式)[设计模式, 迭代器模式, iterator, 迭代器] 设计模式学习笔记迭代器Iterator模式 基本介绍 迭代器案例 类图 实现 ...

  7. 设计模式读书笔记-----工厂方法模式

    一.问题 在前一章<设计模式读书笔记-----简单工厂模式>中通过披萨的实例介绍了简单工厂模式.在披萨实例中,如果我想根据地域的不同生产出不同口味的披萨,如纽约口味披萨,芝加哥口味披萨.如 ...

  8. 《Head first设计模式》学习笔记 – 迭代器模式

    迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示. 爆炸性新闻:对象村餐厅和对象村煎饼屋合并了! 真是个好消息!现在我们可以在同一个地方,享用煎饼屋美味的煎饼早餐,和好吃 ...

  9. 大话设计模式读书笔记

    主题 概要 设计模式 大话设计模式读书笔记 编辑 时间 新建 20170423 序号 参考资料 1 大话设计模式 重新看了一遍设计模式,除了一些已经特别熟悉的模式,都自己敲了一遍代码,有些豁然开朗的感 ...

最新文章

  1. 解压和生成 system.imgdata.img ( ext4格式)
  2. ASP.NET MVC 控制器激活(二)
  3. 二十三、oracle pl/sql分类三 包
  4. MULTISIM仿真2
  5. 一、SIM800C简介
  6. 三菱PLC5u与台达变频器modbus RTU通讯
  7. ex10_11修改Loan类
  8. 小技巧给网页减肥 让网站访问提速
  9. 一种绝对提高开发水平的方法(转)
  10. shape-outside
  11. 找众数的算法代码c语言,寻找众数算法
  12. 嵌入式 配置arm工具链
  13. warning C4183: ‘Cricle‘: member function definition looks like a ctor, but name does not match enclo
  14. apache评分表的意义_APACHE评分系统及评分表 -
  15. 【DS with Python】 Pandas中Series DataFrame的结构、创建、查询、修改语法与实例
  16. php判断显示器横屏还是竖屏,判断横屏竖屏(三种)
  17. 技术研发方面工作经验总结
  18. 用核显能跑matlab吗,5张图告诉你核显究竟能干嘛
  19. CSDN博客运营团队2022年H2总结
  20. 计算机休眠风扇不停,Windows7系统睡眠风扇还在转怎么解决【图文教程】

热门文章

  1. 中国量化融业解金工计机计金领就指
  2. Python库下载安装教程
  3. spark集群环境下Lost task 0.0 in stage 10.0 (TID 17, 10.28.23.202): java.io.FileNotFoundException
  4. git clean 命令详解
  5. Atitit 最近资料文章列表r9 r8 月份 attilax总结
  6. 小型RTK/LITE RTK/Mini RTK CR202(9P+4G+imu) 惯导 实现高精度厘米级定位,输出组合导航位置,定位数据回传服务器
  7. 丘成桐科学奖计算机类,丘成桐科学奖
  8. 跟上司、领导说话的技巧
  9. 【20180904】【查漏补缺】量纲分析法—Pi定理(Buckingham定理)
  10. Vue 计算属性和ref的使用方法