从Java类库看设计模式 7

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()方 法:

1   public boolean contains(Object o) {
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()方法,我们来看看这两个方法是如何实现的:

1   public Iterator iterator() {
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实现,就可以无缝的集成在原来的程序中。

你可能感兴趣的:(java)