本文參(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 模式。