1.1 Iterator 模式
Iterator 模式用于在資料集合中按照順序周遊集合。單詞 Iterate 有反複做某件事情的醫生,漢語稱為“疊代器”。
1.2 示例程式
作用:将書放置到書架(BookShelf)上,并将書的名字按順序顯示出來。

Iterator 示例程式類圖
|| Aggregate 接口
Aggregate 接口是索要周遊的集合的接口。實作了該接口的類将成為一個可以儲存多個元素的集合,像數組一樣。Aggregate 有“使聚集”、“集合”的意思。
/**
*
* describe 生成一個用于周遊集合的疊代器
* @author xmc
* @date 2019/3/19 15:11
*/
public interface Aggregate<T> {
Iterator<T> iterator();
}
在接口中聲明的方法隻有一個 iterator 方法,該方法會生成一個用于周遊集合的疊代器。
|| Iterator 接口
Iterator 接口用于周遊集合中的元素,其作用相當于循環語句中的循環變量。下面定義了最簡單的 Iterator 接口。
/**
*
* describe 最簡單的 Iterator 接口
*/
public interface Iterator<T> {
/**
*
* describe 判斷是否存在下一個元素
* @return boolean
*/
boolean hasNext();
/**
*
* describe 傳回下一個元素的方法
*/
T next();
}
聲明兩個方法,判斷是否存在下一個元素的 hasNext 方法,和擷取下一個元素的 next 方法。
haxNext 方法傳回值是 boolean 類型,當集合中存在下一個元素時,傳回 true,反正不存在,傳回 false,即已經周遊至集合末尾。主要用于循環終止條件。
next 方法傳回的是一個泛型元素,同時還隐含着将疊代器移動至下一個元素的處理。因為 Iterator 接口隻知道方法名,具體的還是需要檢視 Iterator 實作類中的處理。
|| Book 類
Book 類是表示書的類,做的事情隻有一件-通過 getName 方法擷取書的名字。
/**
*
* describe 書 對象
* @author xmc
*/
public class Book {
private String name;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
|| BookShelf 類
BookShelf 類是表示書架的類。需要将該類作為集合進行處理,是以實作了 Aggregate 接口。且還實作了 Aggregate 接口的 iterator 方法。
/**
*
* describe 書架類
* @author xmc
*/
public class BookShelf implements Aggregate<Book>{
private Book[] books;
private int last = 0;
public BookShelf(int size) {
this.books = new Book[size];
}
public Book getBookAt(int index) {
return books[index];
}
// 書架上添加書
public void appendBook(Book book) {
books[last] = book;
last++;
}
public int getLength() {
return last;
}
// 傳回疊代器
@Override
public Iterator<Book> iterator() {
return new BookShelfIterator(this);
}
public static void main(String[] args) {
BookShelf bookShelf = new BookShelf(4);
bookShelf.appendBook(new Book("Around the World in 80 Days"));
bookShelf.appendBook(new Book("Bible"));
bookShelf.appendBook(new Book("Cinderella"));
bookShelf.appendBook(new Book("Daddy-Long-Legs"));
// 擷取疊代器
Iterator<Book> iterator = bookShelf.iterator();
// 如果存在下一個,則會一直循環
while (iterator.hasNext()) {
System.out.println(iterator.next().getName());
}
}
}
書架中定義了 books 字段,它是 Book 類型的數組。在構造器中初始化數組大小。
iterator 方法會生成并傳回 BookShelfIterator 類的執行個體作為 BookShelf 類對應的 Iterator。
|| BookShelfIterator 類
用于周遊書架的 BookShelfIterator 類。
public class BookShelfIterator implements Iterator<Book>{
private BookShelf bookShelf;
private int index;
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
}
/**
*
* describe 判斷是否存在下一個元素
* @return boolean
*/
@Override
public boolean hasNext() {
return (index < bookShelf.getLength());
}
/**
*
* describe 傳回下一個元素的方法
* @return T
*/
@Override
public Book next() {
// 取出目前指向的書,同時令 index 指向下一個
Book book = bookShelf.getBookAt(index);
// 讓疊代器指向下一本書位置
index++;
return book;
}
}
因為BookShelfIterator 類需要發揮 Iterator 的作用,是以它實作了 Iterator 接口。
bookShefl 字段表示 BookShelfIterator 所要周遊的書架。index 字段表示疊代器目前所指向的書的下标。
hasNext 方法将會判斷書架中還有沒有下一本書,如果有就傳回 true,沒有傳回 false。可以通過比較 index 和書架中書的總數來判斷。
next 方法會傳回疊代器目前所指向的書,并讓疊代器指向下一本書位置。
|| Main 類
我們使用 Main 類來制作一個小書架。
public class Main {
public static void main(String[] args) {
BookShelf bookShelf = new BookShelf(4);
bookShelf.appendBook(new Book("Around the World in 80 Days"));
bookShelf.appendBook(new Book("Bible"));
bookShelf.appendBook(new Book("Cinderella"));
bookShelf.appendBook(new Book("Daddy-Long-Legs"));
// 擷取疊代器
Iterator<Book> iterator = bookShelf.iterator();
// 如果存在下一個,則會一直循環
while (iterator.hasNext()) {
System.out.println(iterator.next().getName());
}
}
}
while 部分就是 it.hasNext(),隻要書架上有書,循環就不會停止。
Around the World in 80 Days
Bible
Cinderella
Daddy-Long-Legs
1.3 Iterator 模式中登場的角色
-
Iterator (疊代器)
該角色負責定義按順序逐個周遊元素的接口(API)。在示例中,由 Iterator 接口扮演這個角色,定義了 hasNext 和 next 方法。其中,hasNext 方法用于判斷是否存在下一個元素,next 方法則用于擷取該元素。
-
ConcreateIterator(具體的疊代器)
該角色負責實作 Iterator 角色所定義的接口(API)。在示例程式中,由 BookShelfIterator 類扮演在這個角色。該角色中包含了周遊集合所必須的資訊。
-
Aggregate (集合)
該角色負責定義建立 Iterator 角色的接口(API)。這個接口是一個方法,會建立出“按順序通路儲存在我内部元素”的對象。在示例程式中,由 Aggregate 接口扮演這個角色,它定義了 iterator 方法。
-
ConcreteAggregate (具體的集合)
該角色負責實作 Aggregate 角色所定義的接口。它會建立出具體的 Iterator 角色,即 ConcreteIterator 角色。在示例程式中,由 BookShelf 類扮演這個角色,它實作了 iterator 方法。
Iterator (疊代器)模式
Iterator 模式的類圖
1.4 拓展思路要點
|| 不管實作如何變化,都可以使用 Iterator
為什麼一定要引入 Iterator 這個複雜的設計模式?如果是數組,直接 for 循環進行周遊不就可以了麼?為什麼要在集合之外引入 Iterator 這個角色呢?
一個重要的原因,引入 Iterator 後可以将周遊與實作分離出來。
while (iterator.hasNext()) {
System.out.println(iterator.next().getName());
}
這裡隻使用了 Iterator 的 hasNext 和 next 方法,并沒有調用 BookShelf 的方法。也就是說,這裡的 While 循環并不依賴于 BookShelf 的實作。
如果編寫 BookShelf 的開發人員決定放棄用數組來管理書本,而使用 ArrayList 會怎麼樣?不管 BookShelf 如何變化,隻要 BookShelf 的 iterator 方法能正确的傳回 Iterator 執行個體,即使不對 while 循環做修改,代碼都可以正常工作。這對 BookShelf 的調用者來說真是太友善了。
設計模式的作用就是幫助我們編寫可複用的類。所謂的“可複用”,就是指将類實作為“元件”,當一個元件發生改變時,不需要對其他的元件進行修改或是隻需要很小的修改即可應對。
這樣也就能了解為什麼示例程式中 iterator 方法的傳回值不是 BookSheflIterator 類型而是 Iterator 類型了。這段程式就是要使用 Iterator 的方法進行程式設計,而不是 BookShelfIterator 的方法。
|| 難以了解抽象類和接口
難以了解抽象類和接口的人常使用 ConcreteAggregate 角色和 ConcreteIterator 角色程式設計,而不使用 Aggregate 接口和 Iterator 接口,總想用具體的類來解決所有問題。
但是如果隻使用具體的類來解決問題,很容易導緻類之間的強耦合,這些類也難以作為元件被再次利用。為了弱化類之間的耦合,進而使得類更加容易作為元件被再次利用,我們需要引入抽象類和接口。
“不要隻使用具體的類來程式設計,要優先使用抽象類和接口來程式設計”。
|| Aggregate 和 Iterator 的對應
BookShelfIterator 類知道 BookShelf 類是如何實作的。這樣,我們才知道如何來擷取下一本數的 getBookAt 方法。也就是說,如果BookShelf 的實作發生了改變,即 getBookAt 方法這個接口(API)發生變化時,我們必須修改 BookShelfIterator 類。
正如 Aggregate 和 Iterator 這兩個接口是對應的一樣,ConcreteAggregate 和 ConcreteIterator 這兩個類也是對應的。
|| 多種 Iterator
“将周遊功能置于 Aggregate 角色之外” 是 Iterator 模式的一個特征,可以根據一個 ConcreteAggregate 角色編寫多個 ConcreteIterator 角色。
在示例程式中展示的 Iterator 類隻是簡答地從前向後周遊集合。還可以有其他的周遊方式。
- 從後向前周遊
- 既可以從前向後周遊,也可以從後向前周遊(next 和 previous 方法)
- 指定下标進行“跳躍式”周遊