1.1 乘车买票,不管你是谁!
售票员检查谁没有买票,把车厢里的人都遍历一遍。
1.2 迭代器模式
迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。[DP]
"你想呀,售票员才不管你上来的是人还是物(行李),不管是中国人还是外国人,不管是不是内部员工,甚至哪怕是马上要抓走的小偷,只要是来乘车的乘客,就必须要买票。同样道理,当你需要访问一个聚集对象,而且不管这些对象是什么都需要遍历的时候,你就应该考虑用迭代器模式。另外,售票员从车头到车尾来售票,也可以从车尾向车头来售票,也就是说,你需要对聚集有多种方式遍历时,可以考虑用迭代器模式。由于不管乘客是什么,售票员的做法始终是相同的,都是从第一个开始,下一个是谁,是否结束,当前售到哪个人了,这些方法每天他都在做,也就是说,为遍历不同的聚集结构提供如开始、下一个、是否结束、当前哪一项等统一的接口。"
"哈,本来这个模式还是有点意思的,不过现今来看迭代器模式实用价值远不如学习价值大了,Martin Flower甚至在自己的网站上提出撤销此模式。因为现在高级编程语言如C#、Java等本身已经把这个模式做在语言中了。"
"哦,是什么?"
"哈,foreach你熟悉吗?"
"啊,原来是它,没错没错,它就是不需要知道集合对象是什么,就可以遍历所有的对象的循环工具,非常好用。"
"另外还有像Iterator接口也是为迭代器模式而准备的。不管如何,学习一下GoF的迭代器模式的基本结构,还是很有学习价值的。研究历史是为了更好地迎接未来。"
1.3 迭代器实现
迭代器模式(Iterator)结构图
package code.chapter20.iterator1;import java.util.ArrayList;public class Test {public static void main(String[] args){System.out.println("**********************************************"); System.out.println("《大话设计模式》代码样例");System.out.println(); ConcreteAggregate bus = new ConcreteAggregate();bus.add("大鸟");bus.add("小菜");bus.add("行李");bus.add("老外");bus.add("公交内部员工");bus.add("小偷");//正序迭代器//Iterator conductor = new ConcreteIterator(bus);//倒序迭代器Iterator conductor = new ConcreteIteratorDesc(bus);conductor.first();while (!conductor.isDone()) {System.out.println(conductor.currentItem() + ",请买车票!");conductor.next();}System.out.println();System.out.println("**********************************************");}
}//聚集抽象类
abstract class Aggregate{//创建迭代器public abstract Iterator createIterator();
}//具体聚集类,继承Aggregate
class ConcreteAggregate extends Aggregate{//声明一个ArrayList泛型变量,用于存放聚合对象private ArrayList<Object> items = new ArrayList<Object>();public Iterator createIterator(){return new ConcreteIterator(this);}//返回聚集总个数public int getCount(){return items.size();}//增加新对象public void add(Object object){items.add(object);}//得到指定索引对象public Object getCurrentItem(int index){return items.get(index);}}//迭代器抽象类
abstract class Iterator{public abstract Object first(); //第一个public abstract Object next(); //下一个public abstract boolean isDone(); //是否到最后public abstract Object currentItem(); //当前对象}//具体迭代器类,继承Iterator
class ConcreteIterator extends Iterator{private ConcreteAggregate aggregate;private int current = 0;//初始化时将具体的聚集对象传入public ConcreteIterator(ConcreteAggregate aggregate){this.aggregate = aggregate;}//得到第一个对象public Object first(){return aggregate.getCurrentItem(0);}//得到下一个对象public Object next() {Object ret = null;current++;if (current < aggregate.getCount()) {ret = aggregate.getCurrentItem(current);}return ret;}//判断当前是否遍历到结尾,到结尾返回truepublic boolean isDone(){return current >= aggregate.getCount() ? true : false;}//返回当前的聚集对象public Object currentItem(){return aggregate.getCurrentItem(current);}
}//具体迭代器类(倒序),继承Iterator
class ConcreteIteratorDesc extends Iterator{private ConcreteAggregate aggregate;private int current = 0;public ConcreteIteratorDesc(ConcreteAggregate aggregate){this.aggregate = aggregate;current = aggregate.getCount()-1;}//第一个对象public Object first(){return aggregate.getCurrentItem(aggregate.getCount()-1);}//下一个对象public Object next() {Object ret = null;current--;if (current >= 0) {ret = aggregate.getCurrentItem(current);}return ret;}//判断当前是否遍历到结尾,到结尾返回truepublic boolean isDone(){return current <0 ? true : false;}//返回当前的聚集对象public Object currentItem(){return aggregate.getCurrentItem(current);}
}
Aggregate聚集抽象类:
ConcreteAggregate具体聚集类:继承Aggregate。
Iterator迭代器抽象类:
ConcreteIterator具体迭代器类:继承Iterator。
其实售票员完全可以用更多的方式来遍历乘客,比如从最高的到最矮的、从最小到最老、从最靓丽酷毙到最猥琐龌龊。
1.4 Java的迭代器师兄
"刚才我们也说过,实际使用当中是不需要这么麻烦的,因为Java语言中已经为你准备好了相关接口,你只需去实现就好。"
Java.util.Iterator支持对集合的简单迭代接口。
Java.util.ListIterator支持对集合的任意方向上迭代接口。
"你会发现,这两个接口要比我们刚才写的抽象类Iterator简洁,但可实现的功能却一点不少,这其实也是对GoF的设计改良的结果。"
"其实具体类实现这两个接口的代码也差别不大,是吗?"
"是的,区别不大,另外这两个是可以实现泛型的接口,去查Java的API帮助就可以了。"
"有了这个基础,你再来看你最熟悉的foreach就很简单了。"
"这里用到了foreach而在编译器里做了些什么呢?其实它做的是下面的工作。"
"原来foreach就是实现Iterator来实际循环遍历呀。"
"如果我们想实现刚才的反向遍历。那就用另一个接口实现。"
package code.chapter20.iterator2;import java.util.Iterator;
import java.util.ListIterator;
import java.util.ArrayList;public class Test {public static void main(String[] args){System.out.println("**********************************************"); System.out.println("《大话设计模式》代码样例");System.out.println(); ArrayList<String> bus = new ArrayList<String>();bus.add("大鸟");bus.add("小菜");bus.add("行李");bus.add("老外");bus.add("公交内部员工");bus.add("小偷");System.out.println("foreach遍历:");for(String item : bus){System.out.println(item + ",请买车票!");}System.out.println();System.out.println("Iterator遍历:");Iterator<String> conductor = bus.iterator();while (conductor.hasNext()) {System.out.println(conductor.next() + ",请买车票!");}System.out.println();System.out.println("ListIterator逆向遍历:");ListIterator<String> conductorDesc = bus.listIterator(bus.size());while (conductorDesc.hasPrevious()) {System.out.println(conductorDesc.previous() + ",请买车票!");}System.out.println();System.out.println("**********************************************");}
}public interface Iterator{public boolean hasNext(); //如果迭代具有更多元素,则返回true public Object next(); //返回迭代中的下一个元素}public interface ListIterator{public boolean hasNext(); //如果此列表迭代器在向前遍历列表时具有更多元素,则返回true public Object next(); //返回列表中的下一个元素并前进光标位置public boolean hasPrevious(); //如果此列表迭代器在反向遍历列表时具有更多元素,则返回truepublic Object previous(); //返回列表中的上一个元素并向后移动光标位置}
"是的,尽管我们不需要显式地引用迭代器,但系统本身还是通过迭代器来实现遍历的。总的来说,迭代器(Iterator)模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可以让外部代码透明地访问集合内部的数据。迭代器模式在访问数组、集合、列表等数据时,尤其是数据库数据操作时,是非常广泛的应用,但由于它太普遍了,所以各种高级语言都对它进行了封装,所以反而给人感觉此模式本身不太常用了。"
1.5 迭代高手
"哈哈,看来那个售票员是最了不起的迭代高手,每次有乘客上车他都数数,统计人数,然后再对整车的乘客进行迭代遍历,不放过任何漏网之鱼,啊,应该是逃票之人。"
"隔行如隔山,任何行业都有技巧和经验,需要多思考、多琢磨,才能做到最好的。"
"嗯,编程又何尝不是这样,我相信代码没有最好,只有更好,我要继续努力。"