1、定义
提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示,其别名为游标(Cursor)。迭代器模式是一种对象行为型模式。
2、UML类图
-
抽象容器角色(Aggregate):负责提供创建具体迭代器角色的接口,一般是一个接口,提供一个iterator()方法,例如java中的Collection接口,List接口,Set接口等。
-
具体容器角色(ConcreteAggregate):就是实现抽象容器的具体实现类,比如List接口的有序列表实现ArrayList,List接口的链表实现LinkedList,Set接口的哈希列表的实现HashSet等。
-
抽象迭代器角色(Iterator):负责定义访问和遍历元素的接口。
-
具体迭代器角色(ConcreteIterator):实现迭代器接口,并要记录遍历中的当前位置。
3、实例
a、背景:
正向遍历迭代器实现
-
提供一种可以遍历对象集合的方式,又称为:游标cursor模式
-
聚合对象:存储数据,这里我在代码中定义成Container接口,用于获取迭代器对象
-
迭代器:遍历数据
b、实例
步骤一创建抽象了迭代器类,用于规划有哪些操作
public interface MyIterator {
// 迭代器初始值,如果有的地方需要,移动到-1位置,方便某些操作的统一性
void moveToMinusOne();
boolean moveToFirst(); // 移动游标到第一个位置
boolean next(); // 移动游标到下一个位置
boolean hasNext(); // 判断游标是否有下一个位置
boolean isFirst(); // 判断当前游标是否是第一个位置
boolean isLast(); // 判断当前游标是否是最后一个位置
Object getCurrentObject(); // 获取当前游标所指向的对象
}
步骤二:创建抽象的容器
public interface AggregateContainer {
MyIterator getMyIterator();
}
步骤三:创建具体的容器类实现抽象的聚合容器类,由于迭代器的实现是为了对该具体容器的使用和操作,所以具体迭代器类在具体容器类中以内部类的方式存在,并且实现抽象迭代器接口
public class ConcreteContainer implements AggregateContainer {
private List<Object> list = new ArrayList<>();
public List<Object> getList() {
return list;
}
public void setList(List<Object> list) {
this.list = list;
}
void addObject(Object object) {
list.add(object);
}
void removeObject(Object object) {
list.remove(object);
}
@Override
public MyIterator getMyIterator() {
return new ConcreteIterator();
}
// 使用内部类的定义方式,让ConcreteIterator服务于ConcreteContainer
private class ConcreteIterator implements MyIterator {
private int cursor = -1; // 用于记录遍历时的位置
public void moveToMinusOne() {
cursor = -1;
}
public boolean moveToFirst() {
if (list.size() >= 1) {
cursor = 0;
return true;
}
return false;
}
public boolean next() {
if (hasNext()) {
cursor++;
return true;
}
return false;
}
public boolean hasNext() {
return cursor + 1 < list.size();
}
public boolean isFirst() {
return cursor == 0;
}
public boolean isLast() {
return cursor == list.size() - 1;
}
public Object getCurrentObject() {
if (cursor >= 0 && cursor < list.size())
return list.get(cursor);
else
return null;
}
}
}
步骤四:客户端测试
public class Client {
public static void main(String[] args) {
ConcreteContainer carNameContainer = new ConcreteContainer();
// 测试添加
carNameContainer.addObject("byd");
carNameContainer.addObject("bmw");
carNameContainer.addObject("benz");
carNameContainer.addObject("audi");
// 测试删除
carNameContainer.removeObject("bmw");
// 获取迭代器,测试isFist()与isLast()
MyIterator myIterator = carNameContainer.getMyIterator();
System.out.println("Is first ? : " + myIterator.isFirst());
System.out.println("Move to first : " + myIterator.moveToFirst());
System.out.println("Is first now ? : " + myIterator.isFirst());
// 遍历相关函数测试
System.out.println("--遍历方法1--");
if (myIterator.moveToFirst()) {
do {
System.out.println(myIterator.getCurrentObject());
} while (myIterator.next()); // next()内部先判断是否有next,即hasNext(),有则移动到下一个位置并返回真
}
System.out.println("Is last ? : " + myIterator.isLast());
System.out.println("--遍历方法2--");
if (myIterator.moveToFirst()) {
System.out.println(myIterator.getCurrentObject());
while (myIterator.next()) {
System.out.println(myIterator.getCurrentObject());
}
}
System.out.println("Is last ? : " + myIterator.isLast());
System.out.println("--遍历方法3--");
myIterator.moveToMinusOne();
while (myIterator.next()) {
System.out.println(myIterator.getCurrentObject());
}
System.out.println("Is last ? : " +
myIterator.isLast());
}
}
4、优点
-
简化了遍历方式,对于对象集合的遍历,还是比较麻烦的,对于数组或者有序列表,我们尚可以通过游标来取得,但用户需要在对集合了解很清楚的前提下,自行遍历对象,但是对于hash表来说,用户遍历起来就比较麻烦了。而引入了迭代器方法后,用户用起来就简单的多了。
-
可以提供多种遍历方式,比如说对有序列表,我们可以根据需要提供正序遍历,倒序遍历两种迭代器,用户用起来只需要得到我们实现好的迭代器,就可以方便的对集合进行遍历了。
-
封装性良好,用户只需要得到迭代器就可以遍历,而对于遍历算法则不用去关心。
5、缺点
-
由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
-
抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展,例如JDK内置迭代器Iterator就无法实现逆向遍历,如果需要实现逆向遍历,只能通过其子类ListIterator等来实现,而ListIterator迭代器无法用于操作Set类型的聚合对象。在自定义迭代器时,创建一个考虑全面的抽象迭代器并不是件很容易的事情。
6、适用场景
-
访问一个聚合对象的内容而无须暴露它的内部表示。将聚合对象的访问与内部数据的存储分离,使得访问聚合对象时无须了解其内部实现细节。
-
需要为一个聚合对象提供多种遍历方式。
-
为遍历不同的聚合结构提供一个统一的接口,在该接口的实现类中为不同的聚合结构提供不同的遍历方式,而客户端可以一致性地操作该接口。