迭代器模式/Iterator

意图/适用场景:

迭代器模式的应用在Java语言中司空见惯。

迭代器(Iterator)与聚集(Aggregate)的概念密不可分。聚集是能够包容一组对象的容器对象,不同种类的聚集组织这些对象的方式是不同的,即拥有不同的结构。

Java语言对聚集有良好的支持,Java聚集对象是实现了共同的java.util.Collection接口的对象,包括Vector、ArrayList、Queue、Stack、Hashtable、Hashmap等等。

迭代器的作用是把聚集中的内容转换为线性迭代的形式,这样做的意图有两个:一是把非线性结构转变为线性结构的顺序结构,方便使用者遍历;二是把多种不同的聚集汇聚成统一的接口。

这里描述的迭代器模式与《Java与模式》书中描述稍有不同。即在UML图中,有意把User与聚集隔离开,这样做的目的是突显迭代器在聚集与用户之间的过渡与转换作用。而书中原图则不能表明这一用意。

UML:

迭代器模式/Iterator_设计模式

参与者:

  1. 抽象迭代器(Iterator):迭代器类的公共接口。
  2. 具体迭代器(ConcreteIterator):实现Iterator接口,并保持迭代过程中的游标位置。
  3. 聚集(Aggregate):聚集类的公共接口,定义出创建迭代器的接口。
  4. 具体聚集(ConcreteAggregate):实现Aggregate接口,完成创建迭代器的方法。

要点:

迭代器的用法虽然常见,但实现起来却有很多讲究,这里提及一些所需要考虑的方面,但不作详细讨论:

  1. 正迭代与逆迭代:按照在迭代器中的索引顺序,从低索引值到高索引值的迭代过程称为正迭代,从高索引值到低索引值的迭代过程称为逆迭代。
  2. 宽接口与窄接口:如果一个聚集的接口提供了可以用来修改聚集元素的方法,这个接口就是宽接口;如果 一个聚集的接口没有提供修改聚集元素的方法,这样的接口就是窄接口。
  3. 白盒聚集与黑盒聚集:如果一个聚集提供了访问内部元素的接口,它就称为白盒聚集;如果一个聚集没有向外界提供访问自身内部元素的接口,就称为黑盒聚集。
  4. 外部迭代器与内部迭代器:这一对概念与白/黑盒聚集相关。对于白盒聚集,迭代器的实现可以完全与该聚集的内部结构无关,因为聚集提供了访问内部元素的方法,迭代器只需要调用这些方法就可以实现访问聚集元素的功能,这样的迭代器称为外部迭代器。对于黑盒聚集,因为聚集没有提供访问内部元素的方法,迭代器必须定义为聚集的一个内部类(内部类仍然可以被外部对象访问),这样才能够访问聚集的元素,这样的迭代器称为内部迭代器。
  5. 主动迭代器和被动迭代器:这是相对于客户端来说的。如果客户端控制迭代的进程(如调用next()方法来移动游标),这样的迭代器就是主动迭代器;相反就是被动迭代器。
  6. 静态迭代器与动态迭代器:这一对概念涉及到同步的问题,稍微扩展一下。
    • 静态迭代器持有聚集对象的一个快照,一旦产生后这个快昭的内容就不再变化,原聚集的内容可以被修改,对迭代器不会有影响,但是迭代器对象也就反映不现聚集的最新变化。
    • 静态迭代的好处是安全性和简单性,它易于实现。但坏处是它需要将原聚集复制一份,对时间和内存资源都有消耗,对于大型的聚集对象来说,成本会比较高。
    • 动态迭代器被产生之后,迭代器仍持有对聚集的引用,因此可以反映出聚集的最新情况。
    • 完整的动态迭代器实现起来比较困难,因为要考虑同步与迭代器更新的问题。简化的动态迭代器无法实现迭代器的即时更新,但是应该在聚集元素发生变化时可以得到通知,这就引出另一个概念:Fail Fast。
  7. Fail Fast:
    • 如果当一个算法开始之后,它的运算环境发生变化,使得算法无法进行必需的调整时,这个算法就应当立即发出故障信号。这就是Fail Fast的含义。
    • 如果聚集对象的元素在一个动态迭代器的迭代过程中变化时,迭代过程会受到影响而无法继续,这时候,迭代器就应当立即抛出一个异常。这种迭代器就是实现了Fail Fast功能的迭代器。

示例代码:

Iterator模式在Java类库java.util.Collection中有很好的示例,这里不同提供简化的示例代码。
只给出一个使用Iterator模式的代码,用以明示这一模式的作用。
   [java]
// Source code from file:  User.java

package designPatterns.Iterator;

import java.util.*;

public class User {

public Iterator getIteratorFromHashMap() {
HashMap hashMap = new HashMap();
hashMap.put("One", new Integer(1));
hashMap.put("Two", new Integer(2));
hashMap.put("Three", new Integer(3));
return hashMap.keySet().iterator();
}

public Iterator getIteratorFromList() {
List list = new ArrayList();
list.add("One");
list.add("Two");
list.add("Three");
return list.iterator();
}

public static void main(String[] args) {
User user = new User();
Iterator iteratorFromHashMap = user.getIteratorFromHashMap();
Iterator iteratorFromList = user.getIteratorFromList();
while (iteratorFromHashMap.hasNext()){
System.out.println((String)iteratorFromHashMap.next());
}
while (iteratorFromList.hasNext()){
System.out.println((String)iteratorFromList.next());
}
}

}
[/java]