天天看點

Iterator (疊代器)模式

1.1 Iterator 模式

  Iterator 模式用于在資料集合中按照順序周遊集合。單詞 Iterate 有反複做某件事情的醫生,漢語稱為“疊代器”。

1.2 示例程式

  作用:将書放置到書架(BookShelf)上,并将書的名字按順序顯示出來。

Iterator (疊代器)模式

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 方法)
  • 指定下标進行“跳躍式”周遊

繼續閱讀