本文参(chao)考(xi)《图解设计模式》 结城浩 (做者) 杨文轩 (译者)java
迭代器做用于集合,是用来遍历集合元素的对象。编程
迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。设计模式
迭代器封装了对集合的遍历,使得不用了解集合的内部细节,就可使用一样的方式遍历不一样的集合。数组
迭代器模式属于行为型模式。app
首先,让咱们来看一段实现了Iterator 模式的示例程序。这段示例程序的做用是将书(Book)放置到书架(BookShelf)中,并将书的名字按顺序显示出来:函数
是所要遍历的集合的接口。实现了该接口的类将成为一个能够保存多个元素的集合:测试
1 public interface Aggregate { 2 public abstract Iterator iterator();//生成一个实现了Iterator接口的类的实例 3 }
Iterator 接口用于遍历集合中的元素,其做用至关于循环语句中的循环变量。最简单的Iterator 接口以下:this
1 public interface Iterator { 2 public abstract boolean hasNext(); 3 public abstract Object next(); 4 }
这里咱们声明了两个方法:spa
hasNext()方法:返回值是boolean 类型。其缘由很容易理解,当集合中存在下一个元素时,该方法返回true ;当集合中不存在下一个元素,即已经遍历至集合末尾时,该方法返回false。hasNext 方法主要用于循环终止条件。设计
next()方法:返回类型是Object。这代表该方法返回的是集合中的一个元素。可是,next 方法的做用并不是仅仅如此。为了可以在下次调用next 方法时正确地返回下一个元素,该方法中还隐含着将迭代器移动至下一个元素的处理。说“隐含”,是由于Iterator 接口只知道方法名。想要知道next 方法中到底进行了什么样的处理,还须要看一下实现了Iterator 接口的类(BookShelfIterator)。这样,咱们才能看懂next 方法的做用。
Book 类是表示书的类。提供getName() 方法获取书名。
1 public class Book { 2 private String name; 3 public Book(String name) { 4 this.name = name; 5 } 6 public String getName() { 7 return name; 8 } 9 }
BookShelf 类是表示书架的类。集合Aggregate的实现类。
1 public class BookShelf implements Aggregate { 2 private Book[] books; 3 private int last = 0; 4 public BookShelf(int maxsize) { 5 this.books = new Book[maxsize]; 6 } 7 public Book getBookAt(int index) { 8 return books[index]; 9 } 10 public void appendBook(Book book) { 11 this.books[last] = book; 12 last++; 13 } 14 public int getLength() { 15 return last; 16 } 17 public Iterator iterator() { 18 return new BookShelfIterator(this); 19 } 20 }
Iterator 接口的具体实现方法。
1 public class BookShelfIterator implements Iterator { 2 private BookShelf bookShelf;//遍历对象 3 private int index;//下标 4 public BookShelfIterator(BookShelf bookShelf) { 5 this.bookShelf = bookShelf; 6 this.index = 0; 7 } 8 public boolean hasNext() { 9 if (index < bookShelf.getLength()) { 10 return true; 11 } else { 12 return false; 13 } 14 } 15 public Object next() { 16 Book book = bookShelf.getBookAt(index); 17 index++; 18 return book; 19 } 20 }
构造函数BookShelfIterator():将接收到的BookShelf的实例保存在bookShelf 字段中,并将index 初始化为0。
hasNext()方法:该方法将会判断书架中还有没有下一本书,若是有就返回true,若是没有就返回false。而要知道书架中有没有下一本书,能够经过比较index 和书架中书的总册数(bookShelf.getLength() 的返回值)来判断。
next()方法:会返回迭代器当前所指向的书(Book 的实例),并让迭代器指向下一本书。next()方法稍微有些复杂,它首先取出book 变量做为返回值,而后让index 指向后面一本书。
至此,遍历书架的准备工做就完成了。一下是测试类:
1 public class Main { 2 public static void main(String[] args) { 3 BookShelf bookShelf = new BookShelf(4); 4 bookShelf.appendBook(new Book("Around the World in 80 Days")); 5 bookShelf.appendBook(new Book("Bible")); 6 bookShelf.appendBook(new Book("Cinderella")); 7 bookShelf.appendBook(new Book("Daddy-Long-Legs")); 8 Iterator it = bookShelf.iterator(); 9 while (it.hasNext()) { 10 Book book = (Book)it.next(); 11 System.out.println(book.getName()); 12 } 13 } 14 }
测试输出结果以下:
经过以上示例,让咱们来看看Iterator 模式中的登场角色。
◆ Iterator(迭代器)
该角色负责定义按顺序逐个遍历元素的接口(API)。在示例程序中,由Iterator 接口扮演这个角色,它定义了hasNext 和next 两个方法。其中,hasNext 方法用于判断是否存在下一个元素,next 方法则用于获取该元素。
◆ ConcreteIterator(具体的迭代器)
该角色负责实现Iterator 角色所定义的接口(API)。在示例程序中,由BookShelfIterator 类扮演这个角色。该角色中包含了遍历集合所必需的信息。在示例程序中,BookShelf 类的实例保存在bookShelf 字段中,被指向的书的下标保存在index 字段中。
◆ Aggregate(集合)
该角色负责定义建立Iterator 角色的接口(API)。这个接口(API)是一个方法,会建立出“按顺序访问保存在我内部元素的人”。在示例程序中,由Aggregate 接口扮演这个角色,它里面定义了iterator 方法。
◆ ConcreteAggregate(具体的集合)
该角色负责实现Aggregate 角色所定义的接口(API)。它会建立出具体的Iterator 角色,即ConcreteIterator 角色。在示例程序中,由BookShelf 类扮演这个角色,它实现了iterator 方法。Iterator 模式的类图以下:
无论实现如何变化,均可以使用Iterator
为何必定要考虑引入Iterator 这种复杂的设计模式呢?若是是数组,直接使用for 循环语句进行遍历处理不就能够了吗?为何要在集合以外引入Iterator 这个角色呢?
一个重要的理由是,引入Iterator 后能够将遍历与实现分离开来。请看下面的代码。
1 while (it.hasNext()) { 2 Book book = (Book)it.next(); 3 System.out.println(book.getName()); 4 }
这里只使用了Iterator 的hasNext 方法和next 方法,并无调用BookShelf 的方法。也就是说,这里的while 循环并不依赖于BookShelf 的实现。
若是编写BookShelf 的开发人员决定放弃用数组来管理书本,而是用java.util.Vector取而代之,会怎样呢?无论BookShelf 如何变化,只要BookShelf 的iterator 方法能正确地返回Iterator 的实例(也就是说,返回的Iterator 类的实例没有问题,hasNext 和next 方法均可以正常工做),即便不对上面的while 循环作任何修改,代码均可以正常工做。
这对于BookShelf 的调用者来讲真是太方便了。设计模式的做用就是帮助咱们编写可复用的类。所谓“可复用”,就是指将类实现为“组件”,当一个组件发生改变时,不须要对其余的组件进行修改或是只须要很小的修改便可应对。
这样也就能理解为何在示例程序中iterator 方法的返回值不是BookShelfIterator 类型而是Iterator 类型了(代码清单1-6)。这代表,这段程序就是要使用Iterator 的方法进行编程,而不是BookShelfIterator 的方法。
难以理解抽象类和接口的人经常使用ConcreteAggregate 角色和ConcreteIterator 角色编程,而不使用Aggregate 接口和Iterator 接口,他们总想用具体的类来解决全部的问题。
可是若是只使用具体的类来解决问题,很容易致使类之间的强耦合,这些类也难以做为组件被再次利用。为了弱化类之间的耦合,进而使得类更加容易做为组件被再次利用,咱们须要引入抽象类和接口。
这也是贯穿本书的思想。即便你们如今没法彻底理解,相信随着深刻阅读本书,也必定可以逐渐理解。请你们将“不要只使用具体类来编程,要优先使用抽象类和接口来编程”印在脑海中。
请你们仔细回忆一下咱们是如何把BookShelfIterator 类定义为BookShelf 类的ConcreteIterator 角色的。BookShelfIterator 类知道BookShelf 是如何实现的。也正是由于如此,咱们才能调用用来获取下一本书的getBookAt 方法。
也就是说,若是BookShelf 的实现发生了改变,即getBookAt 方法这个接口(API)发生变化时,咱们必须修改BookShelfIterator 类。
正如Aggregate 和Iterator 这两个接口是对应的同样,ConcreteAggregate 和ConcreteIterator 这两个类也是对应的。
在Iterator 模式的实现中,很容易在next 方法上出错。该方法的返回值究竟是应该指向当前元素仍是当前元素的下一个元素呢?更详细地讲,next 方法的名字应该是下面这样的。
returnCurrentElementAndAdvanceToNextPosition
也就是说,next 方法是“返回当前的元素,并指向下一个元素”。
在Iterator 模式中,不只容易弄错“下一个”,还容易弄错“最后一个”。hasNext 方法在返回最后一个元素前会返回true,当返回了最后一个元素后则返回false。稍不注意,就会没法正确地返回“最后一个”元素。
请你们将hasNext 方法理解成“确认接下来是否能够调用next 方法”的方法就能够了。
“将遍历功能置于Aggregate 角色以外”是Iterator 模式的一个特征。根据这个特征,能够针对一个ConcreteAggregate 角色编写多个ConcreteIterator 角色。
迭代器的种类多种多样
在示例程序中展现的Iterator 类只是很简单地从前向后遍历集合。其实,遍历的方法是多种多样的。
● 从最后开始向前遍历
● 既能够从前向后遍历,也能够从后向前遍历(既有next方法也有previous方法)
● 指定下标进行“跳跃式”遍历
学到这里,相信你们应该能够根据需求编写出各类各样的Iterator 类了。
在Java 中,没有被使用的对象实例将会自动被删除(垃圾回收,GC)。所以,在iterator 中不须要与其对应的deleteIterator 方法。
◆ Visitor 模式
Iterator 模式是从集合中一个一个取出元素进行遍历,可是并无在Iterator 接口中声明对取出的元素进行何种处理。
Visitor 模式则是在遍历元素集合的过程当中,对元素进行相同的处理。
在遍历集合的过程当中对元素进行固定的处理是常有的需求。Visitor 模式正是为了应对这种需求而出现的。在访问元素集合的过程当中对元素进行相同的处理,这种模式就是Visitor 模式。
◆ Composite 模式
Composite 模式是具备递归结构的模式,在其中使用Iterator 模式比较困难。
◆ Factory Method 模式
在iterator 方法中生成Iterator 的实例时可能会使用Factory Method 模式。