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;
}
//判断当前是否遍历到结尾,到结尾返回true
public 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;
}
//判断当前是否遍历到结尾,到结尾返回true
public 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(); //如果此列表迭代器在反向遍历列表时具有更多元素,则返回true
public Object previous(); //返回列表中的上一个元素并向后移动光标位置
}
"是的,尽管我们不需要显式地引用迭代器,但系统本身还是通过迭代器来实现遍历的。总的来说,迭代器(Iterator)模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可以让外部代码透明地访问集合内部的数据。迭代器模式在访问数组、集合、列表等数据时,尤其是数据库数据操作时,是非常广泛的应用,但由于它太普遍了,所以各种高级语言都对它进行了封装,所以反而给人感觉此模式本身不太常用了。"
1.5 迭代高手
"哈哈,看来那个售票员是最了不起的迭代高手,每次有乘客上车他都数数,统计人数,然后再对整车的乘客进行迭代遍历,不放过任何漏网之鱼,啊,应该是逃票之人。"
"隔行如隔山,任何行业都有技巧和经验,需要多思考、多琢磨,才能做到最好的。"
"嗯,编程又何尝不是这样,我相信代码没有最好,只有更好,我要继续努力。"