Iterator模式用来规格化对某一数据结构的遍历接口。
JDK中在Collection Framework中引入了Iterator接口,提供对一个Collection的遍历。每一个Collection类中都定义有从Collection接口中继承而来的iterator()方法,来得到一个Iterator对象,我们称之为遍历器,Iterator接口很简单:
hasNext():用来判断在遍历器中是否还有下一个元素。
next():返回遍历器中的下一个元素。
remove():在被遍历的Collection类中删除最后被返回的那个对象。
我们就以最为常用的Vector为例,看看在Collection Framework中,Iterator模式是如何被实现的。在此之前,我们需要先了解一些Vector和Collection Framework的结构。
Collection接口作为这个Framework的基础,被所有其它的集合类所继承或者实现。对Collection接口,有一个基本的实现是抽象类AbstractCollection,它实现了大部分与具体数据结构无关的操作。比如判断一个对象是否存在于这个集合类中的contains()方法:
2
3 Iterator e = iterator();
4
5 if (o==null) {
6
7 while (e.hasNext())
8
9 if (e.next()==null)
10
11 return true;
12
13 } else {
14
15 while (e.hasNext())
16
17 if (o.equals(e.next()))
18
19 return true;
20
21 }
22
23 return false;
24
25 }
26
27
而这其中调用的iterator()方法是一个抽象方法,有赖于具体的数据结构的实现。但是对于这个containers()方法而言,并不需要知道具体的Iterator实现,而只需要知道它所提供的接口,能够完成某类任务既可,这就是抽象类中抽象方法的作用。其它的在AbstractCollection中实现的非抽象方法,大部分都是依赖于抽象方法iterator()方法所提供的Iterator接口来实现的。这种设计方法是引入抽象类的一个关键所在,值得仔细领悟。
List接口继承Collection接口,提供对列表集合类的抽象;对应的AbstractList类继承AbstractCollection,并实现了List接口,作为List的一个抽象基类。它对其中非抽象方法的实现,也大抵上与AbstractCollection相同,这儿不再赘叙。
而对应于Collection的Iterator,List有其自己的ListIterator,ListIterator继承于Iterator,并添加了一些专用于List遍历的方法:
boolean hasPrevious():判断在列表中当前元素之前是否存在有元素。
Object previous():返回列表中当前元素之前的元素。
int nextIndex():
int previousIndex():
void set(Object o):
void add(Object o):
ListIterator针对List,提供了更为强劲的功能接口。在AbstractList中,实现了具体的iterator()方法和listIterator()方法,我们来看看这两个方法是如何实现的:
2
3 return new Itr(); //Itr是一个内部类
4
5 }
6
7 private class Itr implements Iterator {
8
9 int cursor = 0;//Iterator的计数器,指示当前调用next()方法时会被返回的元素的位置
10
11 int lastRet = -1;//指示刚刚通过next()或者previous()方法被返回的元素的位置,-1
12
13 //表示刚刚调用的是remove()方法删除了一个元素。
14
15 //modCount是定义在AbstractList中的字段,指示列表被修改的次数。Iterator用//这个值来检查其包装的列表是否被其他方法所非法修改。
16
17 int expectedModCount = modCount;
18
19 public boolean hasNext() {
20
21 return cursor != size();
22
23 }
24
25 public Object next() {
26
27 try {
28
29 //get方法仍然是一个抽象方法,依赖于具体的子类实现
30
31 Object next = get(cursor);
32
33 //检查列表是否被不正确的修改
34
35 checkForComodification();
36
37 lastRet = cursor++;
38
39 return next;
40
41 } catch(IndexOutOfBoundsException e) {
42
43 checkForComodification();
44
45 throw new NoSuchElementException();
46
47 }
48
49 }
50
51 public void remove() {
52
53 if (lastRet == -1)
54
55 throw new IllegalStateException();
56
57 checkForComodification();
58
59 try {
60
61 //同样remove(int)也依赖于具体的子类实现
62
63 AbstractList.this.remove(lastRet);
64
65 if (lastRet < cursor)
66
67 cursor--;
68
69 lastRet = -1;
70
71 expectedModCount = modCount;
72
73 } catch(IndexOutOfBoundsException e) {
74
75 throw new ConcurrentModificationException();
76
77 }
78
79 }
80
81 final void checkForComodification() {
82
83 if (modCount != expectedModCount)
84
85 throw new ConcurrentModificationException();
86
87 }
88
89 }
90
91
这儿的设计技巧和上面一样,都是使用抽象方法来实现一个具体的操作。抽象方法作为最后被实现的内容,依赖于具体的子类。抽象类看起来很像是一个介于接口和子类之间的一个东西。
从设计上来讲,有人建议所有的类都应该定义成接口的形式,这当然有其道理,但多少有些极端。当你需要最大的灵活性的时候,应该使用接口,而抽象类却能够提供一些缺省的操作,最大限度的统一子类。抽象类在许多应用框架(Application Framework)中有着很重要的作用。例如,在一个框架中,可以用抽象类来实现一些缺省的服务比如消息处理等等。这些抽象类能够让你很容易并且自然的把自己的应用嵌入到框架中去。而对于依赖于每个应用具体实现的方法,可以通过定义抽象方法来引入到框架中。
其实在老版本的JDK中也有类似的概念,被称为Enumeration。Iterator其实与Enmeration功能上很相似,只是多了删除的功能。用Iterator不过是在名字上变得更为贴切一些。模式的另外一个很重要的功用,就是能够形成一种交流的语言(或者说文化)。有时候,你说Enumeration大家都不明白,说Iterator就都明白了。
Composite,Strategy和Iterator。Composite是一个结构性的模式,用来协调整体和局部的关系,使之能够被统一的安排在一个树形的结构中,并简化了编程。Strategy模式与Bridge模式在结构上很相似,但是与Bridge不同在于,它是一个行为模式,更侧重于结构的语义以及算法的实现。它使得程序能够在不同的算法之间自由方便的作出选择,并能够在运行时切换到其他的算法,很大程度上增加了程序的灵活性。Iterator模式提供统一的接口操作来实现对一个数据结构的遍历,使得当数据结构的内部算法发生改变时,客户代码不需要任何的变化,只需要改变相应的Iterator实现,就可以无缝的集成在原来的程序中。