1. 什么是迭代器模式?
「官方定义:」 迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各种元素,但是又不暴露该对象的内部表示。(它是一种行为型模式)
「使用场景:」 1、访问一个聚合对象的内容而无须暴露它的内部表示。2、需要为聚合对象提供多种遍历方式。3、为遍历不同的聚合结构提供一个统一的接口。
2. 迭代器模式的角色构成
-
迭代器角色(Iterator):定义遍历元素所需要的方法,一般来说会有这么三个方法:取得下一个元素的方法next(),判断是否遍历结束的方法hasNext()),移出当前对象的方法remove(),
-
具体迭代器角色(Concrete Iterator):实现迭代器接口中定义的方法,完成集合的迭代。
-
容器角色(Aggregate): 一般是一个接口,提供一个iterator()方法,例如java中的Collection接口,List接口,Set接口等
-
具体容器角色(ConcreteAggregate):就是抽象容器的具体实现类,比如List接口的有序列表实现ArrayList,List接口的链表实现LinkList,Set接口的哈希列表的实现HashSet等。

3. 迭代器模式在jdk中的应用
在jdk中,与迭代器相关的接口有两个:Iterator 与 Iterable
Iterator:迭代器,Iterator及其子类通常是迭代器本身的结构与方法;Iterable:可迭代的,那些想用到迭代器功能的其它类,如AbstractList HashMap等,需要实现该接口。
「Iterator接口的源码:」
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
「Iterable接口的源码」
public interface Iterable<T> {
Iterator<T> iterator();
}
「迭代器模式的运转思路:」
-
若类A想要使用迭代器,则它的类声明部分为 class A implement Iterable -
在类A实现中,要实现Iterable接口中的唯一方法:Iterator iterator(); 这个方法用于返回一个迭代器,即Iterator接口及其子类; -
在类A中,定义一个内部类S,专门用于实现Iterator接口,定制类A自已的迭代器实现。
4. 迭代器模式的实践
学习完迭代器模式,然后我就写了一个示例程序,示例程序的功能是将书放置到书架中,并将书的名字按顺序显示出来。
4.1 示例程序的类图

4.2 类与接口的说明
名字 | 说明 |
---|---|
Aggregate | 表示集合的接口 |
Iterator | 遍历集合的接口 |
Book | 表示书的类 |
BookShelf | 表示书架的类 |
BookShelfIterator | 遍历书架的类 |
Main | 测试程序行为的类 |
4.3 代码设计
「Aggregate接口」
public interface Aggregate{
public abstract Iterator iterator();
}
在Aggregate接口中声明的方法只有一个iterator方法,该方法会生成一个用于遍历集合的迭代器。想要遍历集合中的元素时,可以调用iterator生成一个实现了Iterator接口的类的实例。
「Iterator接口」
public interface Iterator{
public abstract boolean hasNext();
public abstract Object next();
}
这里的next方法既要能正确返回下一个元素,还要让迭代器移到下一个元素,当然Iterator接口只能只能看到方法名,要想知道next方法的逻辑还是要看看接口的实现类。
-
「易错点:」 一般情况下next方法是返回当前元素,并指向下一个元素。 -
「易错点:」 hasNext方法在返回最后一个元素前会返回true,在返回最后一个元素后会返回false。
「Book类」
public class Book{
private String name;
public Book(String name){
this.name = name;
}
public String getName(){
return name;
}
}
「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;
}
@Override
public Iterator iterator(){
return new BookShelfIterator(this);
}
}
书架类定义了一个Book类型的数组,它实现了Aggregate接口,覆写了iterator方法。该方法生成并返回了BookShelfIterator类的实例作为BookShelf类对应的Iterator。当外部想要便利书架时,就会调用这个方法。
「BookShelfIterator类」
public class BookShelfIterator implements Iterator{
private BookShelf bookShelf;
private int index;
public BookShelfIterator(BookShelf bookShelf){
this.bookShelf = bookShelf;
this.index = 0;
}
@Override
public boolean hasNext() {
if(index < bookShelf.getLength()){
return true;
}else{
return false;
}
}
@Override
public Object next() {
Book book = bookShelf.getBookAt(index);
index++;
return book;
}
}
bookShelf字段表示BookShelfIterator所要遍历的书架,index字段表示迭代器当前所指向书的下标。构造函数会将接收到的BookShelf实例保存到bookShelf字段中,并将index初始化为0。hasNext方法将会判断书架中还有没有下一本书。next方法会返回迭代器当前所指向的书籍,并让迭代器指向下一本书。
「Main类」
public class Main {
public static void main(String[] args) {
BookShelf bookShelf = new BookShelf(4);
bookShelf.appendBook(new Book("三国演义"));
bookShelf.appendBook(new Book("红楼梦"));
bookShelf.appendBook(new Book("西游记"));
bookShelf.appendBook(new Book("水浒传"));
Iterator iterator = bookShelf.iterator();
while(iterator.hasNext()){
Book book = (Book) iterator.next();
System.out.printf(book.getName());
}
}
}

5. 我的理解
迭代器模式与java容器密不可分,他要把遍历算法从容器对象中独立出来。那么为什么要把遍历算从容器对象中独立出来呢? 因为在面向对象设计中,一个难点就是辨认对象的职责。理想的状态下,一个类应该只有一个单一的职责。职责分离可以最大限度的去解耦,但是职责单一说起来容易,做起来难。具体到本模式,我们明显可以看到,一个容器对象它提供了两个职责:一是组织管理数据对象,二是提供遍历算法。所以以Iterator模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明的访问集合内部的数据。
「优点:」 1、它支持以不同的方式遍历一个聚合对象。2、迭代器简化了聚合类。3、在同一个聚合上可以有多个遍历。4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
「缺点:」 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
原文始发于微信公众号(Java之禅):设计模式 | 迭代器模式的学习与思考
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/161381.html