迭代器模式

​ “迭代”这个词并不会陌生,我们经常使用Iterator接口对Collection下的集合类进行遍历,常见代码:

1
2
3
4
Iterator iterator=list.iterator();
while(iterator.hasNext()){
//进行遍历
}

​ 这便是很好的迭代器模式应用例子。我们可以在不暴露集合类的内部结构情况下,通过迭代器让外部客户端透明的访问其内部的数据。

定义及结构

定义

提供一种可以顺序访问聚合对象中的每个元素的方式,而有不暴露该对象的内部结构。又称为“游标cursor模式”。

模式结构

  • 迭代器角色(Iterator):迭代器角色负责定义访问和遍历的接口
  • 具体迭代器角色(ConcreteIterator):具体迭代器角色实现迭代器接口,记录当前的游标位置
  • 聚合角色(Aggregate): 聚合角色负责提供创建具体迭代器角色的接口
  • 具体聚合角色(ConcreteAggregate): 具体聚合角色实现聚合角色接口

结构图

图1

代码

​ 下面通过模仿JavaSDK中的Iterator写一个自己的迭代器。

迭代器角色(Iterator)

1
2
3
4
5
6
7
/**
* 自定义迭代器接口
*/
public interface MyIterator {
boolean hasnext();//是否有下一个元素
Object next();//获取当前游标所指元素,并将游标加1
}

具体聚合角色(ConcreteAggregate)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
* 自定义聚合类
*/
public class ConcreteMyAggregate {
private List<Object> list=new ArrayList<Object>();

public void addObject(Object obj){
this.list.add(obj);
}

public void removeObject(Object obj){
this.list.remove(obj);
}

//获取迭代器
public MyIterator CreateIterator(){
return new ConcreteMyIterator();
}

/**
* 具体迭代器角色
* 使用内部类定义迭代器,可以直接使用外部类属性
*/
private class ConcreteMyIterator implements MyIterator{
private int cursor;//定义游标用于记录遍历时的位置
@Override
public boolean hasnext() {
if(cursor<list.size()){
return true;
}
return false;
}
@Override
public Object next() {
Object obj=list.get(cursor);
if(cursor<list.size()){
cursor++;
}
return obj;
}
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Client {

public static void main(String[] args) {
ConcreteMyAggregate cma=new ConcreteMyAggregate();
cma.addObject("aa");
cma.addObject("bb");
cma.addObject("cc");
cma.addObject("dd");
MyIterator iterator=cma.CreateIterator();
while(iterator.hasnext()){
System.out.println(iterator.next());
}
}
}

控制台

1
2
3
4
aa
bb
cc
dd

总结

​ 迭代器模式是与集合共生共死的,一般来说,我们只要实现一个集合,就需要同时提供这个集合的迭代器,就像java中的Collection,List、Set、Map等,这些集合都有自己的迭代器。假如我们要实现一个这样的新的容器,当然也需要引入迭代器模式,给我们的容器实现一个迭代器。

​ 但是,由于容器与迭代器的关系太密切了,所以大多数语言在实现容器的时候都给提供了迭代器,并且这些语言提供的容器和迭代器在绝大多数情况下就可以满足我们的需要,所以现在需要我们自己去实践迭代器模式的场景还是比较少见的,我们只需要使用语言中已有的容器和迭代器就可以了

优点

  • 简化了遍历方式,对于对象集合的遍历,还是比较麻烦的,对于数组或者有序列表,我们尚可以通过游标来取得,但用户需要在对集合了解很清楚的前提下,自行遍历对象,但是对于hash表来说,用户遍历起来就比较麻烦了。而引入了迭代器方法后,用户用起来就简单的多了。
  • 可以提供多种遍历方式,比如说对有序列表,我们可以根据需要提供正序遍历,倒序遍历两种迭代器,用户用起来只需要得到我们实现好的迭代器,就可以方便的对集合进行遍历了。
  • 封装性良好,用户只需要得到迭代器就可以遍历,而对于遍历算法则不用去关心。

缺点

  • 对于比较简单的遍历(像数组或者有序列表),使用迭代器方式遍历较为繁琐,感觉像ArrayList,我们宁可愿意使用for循环和get方法来遍历集合。