设计模式学习之路 - 迭代器模式 - 封装遍历

来源:互联网 发布:查看linux系统版本位数 编辑:程序博客网 时间:2024/05/11 20:54

今天了解一下迭代器模式。

说到迭代器, 有点编程经验的应该都知道 Iterator..不错,这个就是迭代器。

有时候在走循环流程,我们通常会拿到容器中的迭代器,通过迭代器进行循环。

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

这边举个小例子解释一下。

有两家餐厅要合并了,一个是中式餐馆的, 一个是港式茶点的,因为合并了,两种东西都卖,点餐的人希望同时可以点两种不同的东西。

但是有个问题,在他们的菜单中,尽管菜单的明细情况都一样,其中一个是用数组实现的,一个是却用链表实现的(这里暂且不讨论数组和链表的优劣势)。

这个是菜单的明细,包含了菜品的名字,描述和价格。

package com.chris.iterator;public class MenuItem {String name;String description;double price;public MenuItem(String name, String description, double price) {this.name = name;this.description = description;this.price = price;}public String getName() {return name;}public String getDescription() {return description;}public double getPrice() {return price;}}

中式餐厅的菜单是用数组去保存菜单项的。

package com.chris.iterator;public class ChineseMenu {static final int MAX_ITEMS = 10;int numberOfItems = 0;MenuItem[] menuItems;public ChineseMenu(){menuItems = new MenuItem[MAX_ITEMS];addItem("剁椒鱼头", "湘菜,非常美味的剁椒鱼头", 50);addItem("回锅肉", "特别好吃", 30);addItem("干锅花菜", "用干锅弄的花菜,十分美味", 24);addItem("东坡肉", "苏东坡的肉?", 42);}public void addItem(String name, String description, double price){MenuItem menuItem = new MenuItem(name, description, price);if(numberOfItems >= MAX_ITEMS){System.err.println("sorry, menu is full! Can't add item to menu");}else{menuItems[numberOfItems] = menuItem;numberOfItems++; }}public MenuItem[] getMenuItems(){return menuItems;}}

而港式茶餐厅的菜单时用链表去保存菜单的。

package com.chris.iterator;import java.util.ArrayList;public class HongKongMenu {ArrayList menuItems;public HongKongMenu(){menuItems = new ArrayList();addItem("水晶虾饺", "里面有虾,很好吃", 18);addItem("豉汁蒸排骨", "要等一段时间,但是很美味", 20);addItem("流沙包", "甜甜的,超好吃", 10);addItem("叉烧包", "广东特色,不错", 12);}public void addItem(String name, String description, double price){MenuItem menuItem = new MenuItem(name, description, price);menuItems.add(menuItem);}public ArrayList getMenuItems(){return menuItems;}}

看起来好像没有什么问题,但是每次服务员在展示菜单时,需要先创建两个菜单,再获取里面的菜单,

由于菜单的容器不同,需要 再分别通过两个循环进行展示,打印出菜单的情况(如果再合并一个餐厅,就要三个循环。。)


基于两边菜单都不愿意改变自身的实现,可能由于其他关联太多(实际开发中这种情况也是会有的)。

于是,我们开始考虑其他的解决方案,封装遍历!!

我们只需要封装变化的部分,而这里面的变化,就是两种容器遍历方法和获取元素的方法不同。

链表是通过.size() 和get(index), 数组是通过.length 和 [index],这导致他们的循环不能统一。


我们开始引入迭代器模式,创建迭代器的接口,简单的不能再简单了,只有两个方法,获取下一个元素,判断是否有下一个元素。

package com.chris.iterator;public interface Iterator {boolean hasNext();Object next();}

我们为中式餐厅做一个迭代器,为他的菜单服务,实现了上面接口的两个方法。

package com.chris.iterator;public class ChineseMenuIterator implements Iterator{MenuItem[] items;int position = 0;public ChineseMenuIterator(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 ++;return menuItem;}}

同样的, 也为港式茶餐厅做一个迭代器,为他们的菜单服务。

package com.chris.iterator;import java.util.ArrayList;public class HongKongMenuIterator implements Iterator {ArrayList menuItems;int position = 0;public HongKongMenuIterator(ArrayList menuItems) {this.menuItems = menuItems;}@Overridepublic boolean hasNext() {if (position >= menuItems.size() || menuItems.get(position) == null) {return false;} else {return true;}}@Overridepublic Object next() {MenuItem menuItem = (MenuItem) menuItems.get(position);position++;return menuItem;}}

然后在各自的菜单中,把获取菜单的方法删除, 分别增加一个方法,返回各自的迭代器,这样就OK了。

然后,服务员报菜单就方便多啦,不需要几次循环了,只需要通过统一的遍历将元素循环打印出来,代码瞬间变得优雅起来。

package com.chris.iterator;public class Waitress {ChineseMenu chineseMenu;HongKongMenu hongKongMenu;public Waitress(ChineseMenu chineseMenu, HongKongMenu hongKongMenu){this.chineseMenu = chineseMenu;this.hongKongMenu = hongKongMenu;}public void printMenu(){Iterator chineseMenuIterator = chineseMenu.createIterator();Iterator hongKongMenuIterator = hongKongMenu.createIterator();System.out.println("Chinese food:");printMenu(chineseMenuIterator);System.out.println("HongKong food:");printMenu(hongKongMenuIterator);}private void printMenu(Iterator iterator){while(iterator.hasNext()){MenuItem menuItem = (MenuItem) iterator.next();System.out.print(menuItem.getName() + "-");System.out.print(menuItem.getDescription() + "-");System.out.println(menuItem.getPrice());}}}

测试代码这里就不贴了,应该结果一目了然。


我们只是增加一个接口, 然后通过各自实现接口,将自己的逻辑封装起来,就可以把流程统一起来。

这里就用到面向对象的一个很重要的思想:要针对接口编程,而不要针对实现编程

这也是大家今后在编程的时候要多考虑的地方,这样才能有足够的扩展性,而不是把代码写死。


其实,在java的基本库中就有Iterator接口,而且他相较我们自己写的接口,还多了一个remove方法。

这就是最开始提到的默认的迭代器,有很多容器是在其实现类内部维护一个自身的迭代器, 我们可以通过直接获取这个迭代器进行使用。

而没有的,如果我们需要有类似上述需求的话,也可以自己实现。


迭代器模式就到这了,如果文中有什么不妥甚至错误的地方,还望纠正,和大家共勉!!


0 0
原创粉丝点击