Iterator模式


Java 中可以使用 for 循环语句遍历数组。

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

这里循环变量 i 的作用抽象化、通用化形成的模式,在设计模式中称为 Iterator 模式。Iterator 被称为 “迭代器”。

示例程序


本示例程序的作用是将书(Book)放置到书架(BookShelf)中,将书的名字按顺序显示出来。

该类类图

类和接口一览表

名字说明
Aggregate表示集合的接口
Iterator遍历集合的接口
Book表示书的类
BookShelf表示书架的类
BookShelfIterator遍历书架的类
Main测试程序行为的类

Aggregate接口

public iterface Aggregate {
	public abstract Iterator iterator();
}

如类图所示, Aggregate 接口中声明了一个方法 iterator 聚合了 Iterator 接口。该方法会生成一个抑郁遍历集合的迭代器。

Iterator接口

public interface Iterator {
	public abstract boolean hasNext();
	public abstract Object next();
}

该接口声明了两个方法:

  • hasNext:判断是否存在下一个元素
  • next:获取下一个元素

Book类

public class Book {
	private String name;
	public Book(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
}

此类是最基础的类,基本是被应用的类,提供了基础的方法如下:

  • getName:获取书名
  • 构造方法:设置书名

BookShelf类

public class BookShelf implements Aggregate {
	private Book[] books;
	private int last = 0;
	public BookShelf(int maxsize) {
		this.books = new Book[maxsize];
	}
	public Book getBookAt(int index) {
		return books[index];
	}
	public void appendBook(Book book) {
		this.books[last] = book;
		last++;
	}
	public int getLength() {
		return last;	
	}
	public Iterator iterator() {
		return new BookShelfIterator(this);
	}
}

该方法引用了较多类和接口,具体关系可以参考类图

BookShelfIterator类

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(index);
		index++;
		return book;
	}
}

BookShelfIterator 实现了 Iterator 接口,通过index让循环遍历可以指向下一个下标。

Main

public class Main {
	public static void main(String[] args) {
		BookShelf bookShelf = new BookShelf(4);
		bookShelf.appendBook(new Book("Bible"));
		bookShelf.appendBook(new Book("Bible1"));
		bookShelf.appendBook(new Book("Bible2"));
		bookShelf.appendBook(new Book("Bible3"));
		Iterator it = bookShelf.iterator();
		while (it.hasNext()) {
			Book book = (Book) it.next();
			System.out.ptintln(book.getName());
		}
	}
}

Iterator模式中的登场角色


Iterator 主要有以下角色:

  • Iterator(迭代器)
    • 该角色负责定义按顺序遍历元素的接口。在示例程序中,由Iterator扮演这个角色,它定义 hasNextnext 这两个方法。
  • Concretelterator(具体的迭代器)
  • Aggregate(集合)
    • 该角色负责创建 Iterator 角色的接口。在示例程序中,由Aggregate扮演,它定义了 iterator 方法。
  • ConcreteAggregate(具体的集合)

正常 Iterator 模式的类图:

使用Iterator的要点(为什么使用Iterator)


无论实现如何变化,都可以使用Iterator

为什么不直接使用 for 循环遍历处理呢?而是实现Iterator这个接口呢。
首先,使用 Iterator 可以将遍历与实现分离开

使用 Iterator

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

未使用 Iterator

for (Book book : books) {
	System.out.println(book.getName());
}

在使用Iterator,while循环并不依赖 BookShelf 的实现,如果BookShelf更换 books 的类型为 java.util.Vector,那么 for 将无法工作,需要重新修改逻辑,而使用 iterator 将可以正常工作(前提是hasNext 和 next 方法都可以正常工作)

使用抽象类和接口

通常我们都会使用 ConcreteAggregateConcreteIterator 编程,也就是具体的类来编程,而不使用 AggregateIterator 编程。

如果只使用具体类解决问题,那么类之间将会强耦合,这些类也将难以被二次使用,所以我们需要使用抽象类和接口。

Aggregate和Iterator的对应

示例程序所示,BookShelfIteratorBookShelfConcreteIterator 橘色。如果 BookShelf 的方法修改,那么 BookShelfIterator 同样需要修改,Aggregate 同理。

容易弄错“下一个”

Iterator 模式的实现中,next方法容易出错。该方法的返回值到底是指向当前元素还是当前元素的下一个元素呢?next 方法的名字其实如下:

returnCurrentElementAndAdvanceToNextPosition

也就是说,next方法是 “返回当前的元素,并指向下一个元素”

容易弄错“最后一个”

Iterator 模式中,hasNext 方法在返回最后一个元素前会返回true,当返回了最后一个元素后则返回false。

结尾


通过将 BookShelf 的 books 改为 ArrayList,感受一下 Iterator 模式的丝滑和低耦合的爽感: