目录
- 迭代器模式概述
- 迭代器模式的结构与实现
- 迭代器模式的应用实例
- 使用内部类实现迭代器
- .NET内置迭代器
- 迭代器模式的优缺点与适用环境
迭代器模式概述
电视机遥控器与电视机示意图
分析:
- 电视机<- ->存储电视频道的集合 <- ->聚合类(Aggregate Classes)
- 电视机遥控器 <- -> 操作电视频道<- ->迭代器(Iterator)
- 访问一个聚合对象中的元素但又不需要暴露它的内部结构
聚合对象的两个职责:
- 存储数据,聚合对象的基本职责
- 遍历数据,既是可变化的,又是可分离的
将遍历数据的行为从聚合对象中分离出来,封装在迭代器对象中
由迭代器来提供遍历聚合对象内部数据的行为,简化聚合对象的设计,更符合单一职责原则
迭代器模式的定义:
迭代器模式:提供一种方法顺序访问一个聚合对象中各个元素,且不用暴露该对象的内部表示。Iterator Pattern: Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
对象行为型模式
又名游标(Cursor)模式
通过引入迭代器,客户端无须了解聚合对象的内部结构即可实现对聚合对象中成员的遍历,还可以根据需要很方便地增加新的遍历方式
迭代器模式的结构与实现
迭代器模式包含以下4个角色:
- Iterator(抽象迭代器)
抽象迭代器定义了访问和遍历元素的接口,一般声明如下方法:用于获取第一个元素的first(),用于访问下一个元素的next(),用于判断是否还有下一个元素的hasNext(),用于获取当前元素的currentItem(),在其子类中将实现这些方法。
- ConcreteIterator(具体迭代器)
具体迭代器实现了抽象迭代器接口,完成对聚合对象的遍历,同时在对聚合进行遍历时跟踪其当前对象。
- Aggregate(抽象聚合类)
抽象聚合类用于存储对象,并定义创建相应迭代器对象的接口,声明一个createIterator方法用于创建一个迭代器对象。
- ConcreteAggregate(具体聚合类)
具体聚合类实现了创建相应迭代器的接口,实现了在聚合类中声明的createIterator方法,该方法返回一个与该具体聚合对应的具体迭代器ConcreteIterator实例。
迭代器模式的实现
典型的抽象迭代器代码:
public interface MyIterator {
public void first();
public void next();
public boolean isLast();
public Object currentItem();
}
典型的抽象聚合类代码:
public interface MyCollection {
public MyIterator createIterator();
}
典型的具体迭代器代码:
- 在定义好抽象层之后,我们需要定义抽象迭代器接口和抽象接口的实现类,为了实现方便,一般将具体迭代器作为具体聚合类的内部类,从而迭代器可以直接访问聚合类中的数据。
代码在下面“具体聚合类代码”中体现。
- 除了使用内部类实现之外,也可以使用常规的方式来实现迭代器,代码如下:
抽象迭代器类:
public interface Iterator {
public void first();
public void next();
public boolean isLast();
public Object currentItem();
}
具体迭代器类:
public class ConcreteIterator implements Iterator {
private ConcreteAggregate objects;
public ConcreteIterator(ConcreteAggregate objects) {
this.objects = objects;
}
@Override
public void first() {
}
@Override
public void next() {
}
@Override
public boolean isLast() {
return false;
}
@Override
public Object currentItem() {
return null;
}
}
抽象聚合类:
public interface Aggregate {
public Iterator createIterator();
}
具体聚合类:
public class ConcreteAggregate implements Aggregate {
@Override
public Iterator createIterator() {
return new ConcreteIterator(this);
}
}
典型的具体聚合类代码:
public class NewCollection implements MyCollection {
private Object[] obj = { "dog", "pig", "cat", "monkey", "pig" };
@Override
public MyIterator createIterator() {
return new NewIterator();
}
private class NewIterator implements MyIterator {
private int currentIndex = 0;
@Override
public void first() {
currentIndex = 0;
}
@Override
public void next() {
if (currentIndex < obj.length) {
currentIndex++;
}
}
@Override
public boolean isLast() {
return currentIndex == obj.length;
}
@Override
public Object currentItem() {
return obj[currentIndex];
}
}
}
典型的客户端类代码:
public class Client {
public static void process(MyCollection collection) {
MyIterator i = collection.createIterator();
while (!i.isLast()) {
System.out.println(i.currentItem().toString());
i.next();
}
}
public static void main(String[] args) {
MyCollection collection = new NewCollection();
process(collection);
}
}
迭代器模式的应用实例
实例:电视机遥控器
•电视机遥控器就是一个迭代器的实例,通过它可以实现对电视机频道集合的遍历操作,本实例我们将模拟电视机遥控器的实现。
实例类图:
实例说明:
某软件公司为某商场开发了一套销售管理系统,在对该系统进行分析和设计时,开发人员发现经常需要对系统中的商品数据、客户数据等进行遍历,为了复用这些遍历代码,开发人员设计了一个抽象的数据集合类AbstractObjectList,将存储商品和客户等数据的类作为其子类,AbstractObjectList类结构如下图所示:
AbstractObjectList类结构图
在图中,List类型的对象objects用于存储数据,其方法与说明如下表所示:
AbstractObjectList类的方法与说明
AbstractObjectList类的子类ProductList和CustomerList分别用于存储商品数据和客户数据。
通过分析,发现AbstractObjectList类的职责非常重,它既负责存储和管理数据,又负责遍历数据,违背了单一职责原则,实现代码将非常复杂。因此,开发人员决定使用迭代器模式对AbstractObjectList类进行重构,将负责遍历数据的方法提取出来,封装到专门的类中,实现数据存储和数据遍历分离,还可以给不同的具体数据集合类提供不同的遍历方式。
现给出使用迭代器模式重构后的解决方案。
实例类图:
实例代码
- (1) AbstractObjectList:抽象聚合类
- (2) ProductList:商品数据类,充当具体聚合类
- (3) AbstractIterator:抽象迭代器
- (4) ProductIterator:商品迭代器,充当具体迭代器
- (5) Program:客户端测试类
结果及分析
- 如果需要增加一个新的具体聚合类,只需增加一个新的聚合子类和一个新的具体迭代器类即可,原有类库代码无须修改,符合开闭原则
- 如果需要更换一个迭代器,只需要增加一个新的具体迭代器类作为抽象迭代器类的子类,重新实现遍历方法即可,原有迭代器代码无须修改,也符合开闭原则
- 如果要在迭代器中增加新的方法,则需要修改抽象迭代器的源代码,这将违背开闭原则
Java内置迭代器
JDK 1.2 引入了新的Java聚合框架Collections
- Collection是所有Java聚合类的根接口。
- 在JDK类库中,Collection的iterator()方法返回一个java.util.Iterator类型的对象,而其子接口java.util.List的listIterator()方法返回一个java.util.ListIterator类型的对象,ListIterator是Iterator的子类。它们构成了Java语言对迭代器模式的支持,Java语言的java.util.Iterator接口就是迭代器模式的应用。
在JDK中,Iterator接口具有如下3个基本方法:
- (1) Object next():通过反复调用next()方法可以逐个访问聚合中的元素。
- (2) boolean hasNext():hasNext()方法用于判断聚合对象中是否还存在下一个元素,为了不抛出异常,必须在调用next()之前先调用hasNext()。如果迭代对象仍然拥有可供访问的元素,那么hasNext()返回true。
- (3) void remove():用于删除上次调用next()时所返回的元素。
Java迭代器可以理解为它工作在聚合对象的各个元素之间,每调用一次next()方法,迭代器便越过下个元素,并且返回它刚越过的那个元素的地址引用。但是,它也有一些限制,如某些迭代器只能单向移动。在使用迭代器时,访问某个元素的唯一方法就是调用next()。
代码示例:
Iterator iterator = collection.iterator(); //collection是已实例化的集合对象
iterator.next(); // 跳过第一个元素
iterator.remove(); // 删除第一个元素
迭代器模式的优缺点与适用环境
模式优点
- 支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式
- 简化了聚合类
- 由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无须修改原有代码,符合开闭原则
模式缺点
- 在增加新的聚合类时需要对应地增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性
- 抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展。在自定义迭代器时,创建一个考虑全面的抽象迭代器并不是一件很容易的事情
模式适用环境
- 访问一个聚合对象的内容而无须暴露它的内部表示
- 需要为一个聚合对象提供多种遍历方式
- 为遍历不同的聚合结构提供一个统一的接口,在该接口的实现类中为不同的聚合结构提供不同的遍历方式,而客户端可以一致性地操作该接口