軟體開發之 Iterator 模式 (疊代器)

(參考自設計模式)
在設計程式的過程中,經常有的需求之一,就是希望逐一取得某物件內部的所有資料(或物件),像是取得ArrayList中所有 的資料,或取得HashSet中所有的資料。

由於物件在實作內部資料的組織時方式不盡相同,因此也只有物件本身才知道如何收集內部資料,因此Iterator的實作,通常會是物件的內部類別,外界無 需關心,只要知道如何操作Iterator即可。

以Java 的Collection API設計來說

在JDK 1.4時,iterator()方法是定義在Collection介面上,每個Collection的實現類別,都會有iterator()方法,在 JDK5之後,則將iterator()方法定義在Iterable介面上,而Collection介面則繼承了Iterable介面:

例如,您也許會希望設計一個foreach方法,可以將丟給它的物件中的資料逐一取得並顯示在主控台中:

List list = new ArrayList();
...
foreach(list);

Set set = new HashSet();
....
foreach(set);

因為List是有序結構並有索引特性,而Set則為無序不重複的特性,兩者所提供的公開存取方法也不相同,如何將foreach方法設計的通用是個問題。

無論是List或Set,都有個iterator()方法可以傳回一個Iterator物件,這個物件會收集List或Set物件內部資料,並有 hasNext()、next()方法可以使用,而實際上,這個方法是繼承自Collection介面(List與Set的父介面),您可以這麼設計 foreach方法:

public static void foreach(Collection collection) {
    Iterator iterator = collection.iterator();
    while(iterator.hasNext()) {
        System.out.println(iterator.next());
    }
}

這是Iterator模式的實現,不同的物件內部在組織資料方式並不相同(陣列?鏈結?雜湊?),所提供的公開存取介面也不一樣,為了有一致的方式來逐一取 得物件內部的資料,您可以讓一個Iterator於物件內部進行收集,之後傳回Iterator物件,透過該Iterator來逐一取得物件內部資料。

其他語言的一些iterator的範例:

C#

// Method that takes an iterable input (possibly an array)
// and returns all even numbers.
public static IEnumerable GetEven(IEnumerable numbers)
{
    foreach(int i in numbers)
    {
        if (i % 2 == 0) yield return i;
    }
}

C++

template
void printall(InputIterator first, InputIterator last)
{
    for(; first != last; ++first)
    {
        std::cout << *first << std::endl;
    }
}

JAVA:

Iterator iter = list.iterator();
//Iterator iter = list.iterator();    in J2SE 5.0
while (iter.hasNext())
    System.out.println(iter.next());

PHP

class a implements Iterator {
  var $keys = array("k1", "k2", "k3");
  var $vals = array("v1", "v2", "v3");
  var $pos = 0;
  function current() {
    return $this->vals[$this->pos];
  }
  function key() {
    return $this->keys[$this->pos];
  }
  function next() {
    $this->pos++;
  }
  function rewind() {
    $this->pos = 0;
  }
  function valid() {
    if($this->pos>=count($this->keys)) {
      return false;
    }
    else {
      return true;
    }
  }
}
$a = new a;
foreach($a as $k=>$v) echo "$k:::$v\n";

執行結果:
Feng-Hsu-Pingteki-MacBook-Air:ironman6 fillano$ php 1-4a.php k1:::v1 k2:::v2 k3:::v3

發表迴響