版权声明:本文为 冬夏 原创文章,可以随意转载,但请注明出处。
迭代器模式:提供了一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
迭代器模式(Iterator)是我们在日常编程时经常使用到的模式,比如在遍历某个List或者Map的时候。
首先我们来看一下迭代器模式的 UML 图
从 UML 图中我们可以看出迭代器模式的几个特点:
- 迭代器是一个接口,定义了遍历聚合对象的操作,比如next()方法。
- 由具体的聚合类实现各自的迭代器,从而把迭代操作的具体实现屏蔽起来。
- 所有的聚合类都有生成迭代器的方法可以生成对应的迭代器。
分析完迭代器模式的特点,我们可以通过一个简单的例子来具体说明迭代器模式是如何工作的。
假设我们要使用迭代器模式遍历一个名字列表,首先应该创建迭代器结构,定义了如何遍历列表。
public interface Iterator {
boolean hasNext();
Object next();
}
接下来定义容器类接口,包含了生成迭代器的方法
public interface Container {
Iterator getIterator();
}
接下来定义具体的容器类,每个容器类各自实现对应的迭代器类
public class NameRepository implements Container {
public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};
@Override
public Iterator getIterator() {
return new NameIterator();
}
private class NameIterator implements Iterator {
int index;
@Override
public boolean hasNext() {
if(index < names.length){
return true;
}
return false;
}
@Override
public Object next() {
if(this.hasNext()){
return names[index++];
}
return null;
}
}
}
这样我们就可以简单地实现遍历名字列表的功能了
public class Main {
public static void main(String[] agrs){
NameRepository namesRepository = new NameRepository();
for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){
String name = (String)iter.next();
System.out.println("Name : " + name);
}
}
}
可能到这里你会有一个疑问,我可以直接使用数组存放名字,然后通过数组下标遍历名字,这样不是更加简单吗?当然可以,但是我们说好的程序应该有以下几个特点:
- 易于扩展
- 耦合性低
如果你用了数组实现了遍历,那么如果下一次需要添加一个删除某个位置插入或删除一个名字的功能,那么用数组实现的复杂度是O(n),而用链表实现的复杂度是O(1),这个时候你可能会想使用链表来实现,但是你前面的所有功能都是基于数组实现的,所以你不得不全部重写一遍。而如果你使用了迭代器模式,因为客户端只是使用了getIterator()方法生成迭代器,并没有暴露出具体的实现,所以只需要修改聚合类内部的迭代器实现就可以了。
但是迭代器模式也有一些缺点
- 由于每一个具体的聚合类都要实现对应的迭代器,所以代码量会比较多,略显繁琐。
- 由于迭代器提供了一种方法去访问聚合类的元素,在一定程度上破坏了聚合类的封闭性。
最后,如果你去仔细看看 Java 的源码,会发现其实 Java 源码已经实现了迭代器模式了。其中 Collection 接口就是聚合类接口, Iterator 接口就是迭代器接口,List,Map等类就是具体的聚合类,每个聚合类内都实现了各自的迭代器类。