迭代器模式
1、前言
类似于Java语言中的for循环遍历一个一个数组,如下面这段程序:
for(int i = 0; i < arr.length;i++){ System.out.println(arr[i]); }
将循环变量元素i抽象化的模式就称为迭代器模式(Iterator模式)。迭代器模式可以按照指定的顺序规则遍历指定的集合。
2、示例程序
2.1、UML类图
-
类和接口一览表
类或接口名 说明 Aggregate 一个接口,表明实现该接口的类是一个集合类,并提供一个可以遍历该集合的迭代器 Iterator 遍历集合的接口 BookShelf 表示书架的类 BookShelfIterator 遍历书架的类 Book 表示书的类 Main 测试程序
2.2、示例程序
-
Aggregate接口
Aggregate接口会生成一个遍历集合的迭代器。
示例程序:
/** * 表明实现本接口的类是一个集合类 */ public interface Aggregate { /** * 获取能遍历实现类集合的迭代器 * @return */ Iterator iterator(); }
-
Iterator接口
Iterator接口用于遍历集合中的元素。其中,hasNext()方法用于判断集合中是否还有元素;next()方法用于获取集合中当前索引处的元素,并将索引指向下一处。
示例程序:
public interface Iterator { /** * 是否还有下一元素 * @return */ boolean hasNext(); /** * 取出下一元素 * @return */ Object next(); }
-
BookShelf类
BookShelf类实现了Aggregate接口,实现了iterator方法,并还提供了遍历集合所需要的必要方法。
示例程序:
public class BookShelf implements Aggregate{ private Book[] books; private int last = 0; /** * 初始化书架大小 * @param size */ public BookShelf(int size) { this.books = new Book[size]; } /** * 向书架放书 * @param book */ public void appendBook(Book book){ this.books[last] = book; last++; } /** * 获取指定索引处的书籍 * @param index * @return */ public Book getBookAt(int index){ return this.books[index]; } /** * 返回书架容量大小 * @return */ public int getLength(){ return last; } /** * 返回遍历本书架的一个迭代器 * @return */ @Override public Iterator iterator() { return new BookShelfIterator(this); } }
-
BookShelfIterator类
BookShelfIterator类实现了Iterator接口,专门用于遍历BookShelf集合中的元素。
示例程序:
public class BookShelfIterator implements Iterator { private BookShelf bookShelf; private int index = 0; public BookShelfIterator(BookShelf bookShelf) { this.bookShelf = bookShelf; } /** * 判断书架是否还有书 * @return */ @Override public boolean hasNext() { if(index < bookShelf.getLength()){ return true; } return false; } /** * 获取下一个元素 * @return */ @Override public Object next() { Book book = bookShelf.getBookAt(index); index++; return book; } }
-
Book类
public class Book { private String name; public Book(String name) { this.name = name; } public String getName() { return name; } }
-
测试类Main
public class App { public static void main(String[] args) { BookShelf bookShelf = new BookShelf(5); // 向书架添加书籍 bookShelf.appendBook(new Book("Java从入门到入土")); bookShelf.appendBook(new Book("三国演义")); bookShelf.appendBook(new Book("红楼梦")); bookShelf.appendBook(new Book("水浒传")); bookShelf.appendBook(new Book("西游记")); Iterator iterator = bookShelf.iterator(); // 遍历书架 while (iterator.hasNext()){ Book next = (Book) iterator.next(); System.out.println(next.getName()); } } }
-
测试结果
3、迭代器模式中的角色
-
Iterator(迭代器)
该角色负责定义迭代器的扫描规则及获取元素的方法。示例程序中,由Iterator接口扮演这个角色。
-
Aggregate(集合)
该角色负责定义可以返回遍历本集合的迭代器的方法。在示例程序中,由Aggregate接口扮演这个角色。
-
ConcreteIterator(具体的迭代器)
该角色负责实现Iterator角色的定义的方法,该角色还要提供遍历集合所必要的信息,比如书架–bookShelf、遍历索引–index。在示例程序中,由BookShelfIterator类扮演此角色。
-
ConcreteAggregate类
该角色负责实现Aggregate接口中的方法,它会返回具体的迭代器。在示例程序中,由BookShelf类扮演此角色。
迭代器模式的UML类图
4、拓展思路
4.1、不管实现如何,都可以使用Iterator
迭代器的作用就是用来遍历集合的,那为什么不直接用for循环遍历集合呢,还非得多此一举?
迭代器的一大作用就是它可以让集合的遍历与集合的实现进行分离,请看如下代码:
// 遍历书架
while (iterator.hasNext()){
Book next = (Book) iterator.next();
System.out.println(next.getName());
}
迭代器遍历集合的代码是没有BookShelf这个类的,也即迭代器遍历是不依赖于集合类的。因此如果后期集合类存储元素的容器不使用数组,改为使用List集合了,那么上面这段遍历集合的代码仍然可以使用。这也就体现了一个类作为一个组件的可复用性–这个组件的修改,不会影响或者说很少影响其他的组件。
4.2、面向接口(抽象)编程
可以看到iterator()方法在返回迭代器时,返回类型是Iterator,而不是BookShelfIterator。这表明这段程序强调的是要使用Iterator的方法而不是BookShelfIterator中特有的方法,且这样可以弱化类之间的耦合程度,提升这段程序的可用性。
4.3、迭代器中的方法
1、next()方法很容易让人搞混,到底是返回当前元素,还是返回下一个元素呢?其实next()方法是返回当前元素,并指向下一元素。
2、hasNext()方法。这个方法也容易搞混,当索引遍历至最后一个元素时,这时该方法是返回true还是返回false呢?答案是会返回true。它并不是判断当前集合中是否还有下一个元素,他是判断当前集合是否还允许再次调用next()方法。因此当next()方法返回元素后,再次调用hasNext()方法便返回false。
5、总结
迭代器模式就是用于遍历集合中的元素,其一大好处是将集合的遍历与集合的实现方式进行了分离,不论集合存储元素的方式如何变化,集合的遍历方式都不会受到影响。