Iterator模式

看《图解设计模式》一书,做个笔记。

如何遍历一个数组?大多数语言可以使用 for 循环来轻松实现这一需求。for 循环我们可能再熟悉不过了。

1
2
3
for ( int i = 0; i < arr.length; ++i){
System.out.println(arr[i]) //java
}

Python语言中有迭代器这一说,用来遍历可迭代的对象。例如以下例程:

1
2
3
4
5
6
it = iter( (1, 3, 5, 7, 9) ) #生成迭代器对象
while True:
try:
print( next(it) ) #获得下一个值
except StopIteration: #捕获停止迭代的异常,跳出循环
break

对于迭代器,就是实现遍历可迭代对象的工作。这个可迭代对象,个人理解可以是数组,集合,或者是一些其他复杂的结构。所有的这种可迭代对象都可以抽象为 next()hasNext() 即下一个和判断是否有下一个元素的两个方法。(比如上面 python 语言中 next() 函数和 StopIteration 异常就是对应的这两个方法,只不过实现起来不同语言可能不一样)。所以,对于迭代器设计模式,我们可以首先抽象出 Iterator 接口。

1
2
3
4
5
6
7
//Iterator.java
package com.company;

public interface Iterator {
public abstract boolean hasNext(); //判断对象是否迭代结束
public abstract Object next(); //返回当前元素,并将指针向后移动一个单位
}

这样,我们抽象出了 Iterator接口 ,对于可迭代的对象,我们可以使用实现这个借口的方式进而实现我们的迭代器设计模式。下面我们用书架和书这个关系来说说这个迭代器模式的设计思路。(参考书目《图解设计模式》)
首先,我们可以定义书这个class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Book.java
package com.company;

public class Book {
private String name;

public Book (String name){
this.name = name;
}

public String getName () {
return this.name;
}
}

接着,在声明书架这个 class 之前,我们还要定义一个 Aggregate 接口,个人理解这个借口的作用是用来生成迭代器对象的接口。

1
2
3
4
5
6
7
//Aggregate.java
package com.company;

//表示集合的接口
public interface Aggregate {
public abstract Iterator iterator();
}

接下来就是实现书架这一class,来实现上面的 Aggregate 接口。

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
//BookShelf.java
package com.company;

public class BookShelf implements Aggregate {
private Book[] books; //数组来储存书的条目
private int last = 0; //last来标记books[]最末元素的索引值

public BookShelf (int maxSize){
this.books = new Book[maxSize];
}

public Book getBookAt (int index){
return this.books[index];
}

public void appendBook (Book book){
this.books[last] = book;
++last;
}

public int getLength () {
return this.last;
}

public Iterator iterator () {
return new BookShelfIterator(this);
}
}

最后的 iterator() 返回了 BookShelfIterator 对象,也就是我们一直说的迭代器,接下来通过实现 Iterator 来实现 BookShelfIterator 对象。

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
//BookShelfIterator.java
package com.company;

public class BookShelfIterator implements Iterator{
private BookShelf bookShelf;
private int index;

public BookShelfIterator (BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
}

public boolean hasNext () {
if(index < bookShelf.getLength())
return true;
else
return false;
}

public Object next () {
Book book = bookShelf.getBookAt(this.index);
index++;
return book;
}
}

这个 BookShelfIterator class 具体的实现了 Iterator 接口中的两个方法,即可通过 Aggregate 中的 iterator() 生成迭代器,在通过 next()hasNext() 来实现迭代,在 Main class 中可以对我们的代码进行测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.company;

public class Main {

public static void main(String[] args) {

BookShelf bookShelf = new BookShelf(4);
bookShelf.appendBook( new Book("Bible") );
bookShelf.appendBook( new Book("The Martian") );

Iterator it = bookShelf.iterator();

while ( it.hasNext() ){
Book book = (Book) it.next();
System.out.println( book.getName() );
}
}
}

最后说说我的对 Iterator 模式的理解,这可以使得不同的可迭代对象比如,Vector,ArrayList 什么鬼的东西,使用者只需要 hasNext() 和 next() 两个 function 来遍历即可。具体的实现交给 BookShelfIterator class,我们只需要编写不同的 class 来实现不同的需求,甚至我们可以自定义迭代的规则,正序或反序,或者“跳跃式“迭代等等需求。然而这些大大方便了使用者去调用。

最后附上书中的两张图