前言
迭代器用于对一个聚合对象进行遍历。通过引入迭代器可以将数据的遍历功能从聚合对象中分离出来,聚合对象只负责存储数据,而遍历数据由迭代器完成,简化了聚合对象的设计,更符合单一职责原则的要求。
正文
在软件系统中,聚合对象拥有两个职责:一是存储数据;二是遍历数据。从依赖性看,前者时聚合对象的基本职责;而后者既是可变化的,又是可分离的。
一、定义
定义如下:
迭代器模式: 提供一种方法顺序访问一个聚合对象中的各个元素,而又不用暴露该对象的内部表示。
Iterator Pattern: Provide a way to access the elements of an aggregate object sequentially without
exposing its underlying reppresentation.
迭代器模式又称游标(Cursor)模式,它是一种对象行为型模式。
二、结构
迭代器模式结构中包含了聚合和迭代器两个层次结构,考虑到系统的灵活性和可扩展性,应用了工厂方法模式,迭代器模式包含了4个角色:
(1)Iterator(抽象迭代器):它定义了访问和遍历元素的接口,声明了用于遍历数据元素的方法。
(2)ConcreteIterator(具体迭代器):它实现了抽象迭代器接口,完成对聚合对象的遍历,同时在具体迭代器中通过游标来记录在聚合对象中所处的当前位置,在具体实现时游标通常是一个表示位置的非负整数。
(3)Aggregate(抽象聚合类):它用于存储和管理元素对象,声明一个createiterator()方法用于创建一个迭代器对象,充当抽象迭代器工厂角色。
(4)ConcreteAggregate(具体聚合类):它是抽象聚合类的子类,实现了在抽象聚合类中声明的createIterator()方法,该方法返回一个与该具体聚合类对应的具体迭代器ConcreteIterator实例。
三、实现
抽象迭代器,典型代码如下:
public interface Iterator{
public void first(); //将游标指向第一个元素
public void next(); //将游标指向下一个元素
public boolean hasNext(); //判断是否存在下一个元素
public Object currentItem(); //获取游标指向的当前元素
}
具体迭代器,典型代码如下:
public class ConcreteIterator implements Iterator{
private ConcreteAggregate objects; //维持一个对具体聚合对象的引用,以便于访问存储在聚合对象中的数据
private int cursor; //定义一个游标,用于记录当前访问位置
public ConcreteIterator(ConcreteAggregate objects){
this.objects = objects;
}
public void first(){...}
public void next(){...}
public boolean hasNext(){...}
public Object currentItem(){...}
}
使用抽象类设计抽象迭代器时,增加新操作将违反开闭原则,因此设计时要考虑全面,避免修改接口。
最简单的抽象聚合类代码如下:
public interface Aggregate{
Iterator createIterator();
}
具体聚合类典型代码如下:
public class ConcreteAggregate implements Aggregate{
...
public Iterator createIterator(){
return new ConcreteIterator(this);
}
...
}
四、使用内部类实现迭代器
在迭代器模式结构中,具体迭代器类和具体聚合类之间存在关联关系,为了访问存储在聚合对象中的数据,以便迭代器可以对这些数据进行遍历操作。此外,还可以将迭代器类设计为聚合类的内部类,JDK中的迭代器类就是通过这种方法来实现的,如AbstractLlist类代码片段:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
...
private class Itr implements Iterator<E>{
int cursor = 0;
...
}
}
五、Java内置迭代器
在Java集合框架中,常用的List和Set等聚合类都继承(或实现)了java.util.Collection接口,在Collection接口中声明了以下方法(部分):
public interface Collection<E> extends Iterable<E>{
..
boolean add(Object c);
boolean addAll(Collection c);
boolean remove(Object o);
boolean removeAll(Collection c);
boolean remainAll(Collection c);
Iterator iterator();
...
}
在JDK中定义了抽象迭代器接口Iterator,代码如下:
public interface Iterator<E>{
boolean hasNext();
E next();
void remove();
}
六、迭代器模式优缺点与适用环境
优点
(1)迭代器模式支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式。在迭代器模式中只需要一个不同的迭代器来替换原有迭代器即可改变遍历算法,也可以自己定义迭代器的子类以支持新的遍历方式。
(2)迭代器模式简化了聚合类。由于引入了迭代器,在原有的聚合对象中不需要再自行提供数据遍历等方法,这样可以简化设计。
(3)在迭代器模式中由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无须修改原有代码,满足开闭原则。
缺点
(1)由于迭代器模式将存储数据和遍历数据的职责分离,在增加新的聚合类时需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
(2)抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展。在自定义迭代器时考虑一个全面的抽象迭代器并不容易。
适用环境
(1)访问一个聚合对象的内容而无须暴露它的内部表示。将聚合对象的访问与内部数据的存储分离,使得访问聚合对象时无须了解其内部实现细节。
(2)需要为一个聚合对象提供多种遍历方式。
(3)为遍历不同的聚合结构提供一个统一的接口,在该接口的实现类中为不同的聚合接口提供不同的遍历方式,而客户端可以一致性地操作该接口。
以上文字,大量摘抄自《Java设计模式》一书,由刘伟老师编著,故本文应当列入转载一类,有兴趣的朋友可以直接阅读原书。