《PHP設計模式介紹》第八章 迭代器模式(3)_PHP教程
推薦:《PHP設計模式介紹》第七章 策略模式在編寫面向?qū)ο蟮拇a的時,有些時候你需要一個能夠自己根據(jù)不同的條件來引入不同的操作對象實例。例如,一個菜單功能能夠根據(jù)用戶的“皮膚”首選項來決定是否采用水平的還是垂直的排
| class IteratorTestCase extends UnitTestCase { protected $lib; function setup() { /* ... */ } function TestGetGofIterator() { /* ... */ } function TestGofIteratorUsage() { $output = ‘’; for ($it=$this->lib->getIterator(); !$it->isDone(); $it->next()){ $output .= $it->currentItem()->name; } $this->assertEqual(‘name1name2name3’, $output); } } |
目前,迭代器的實現(xiàn)復制了某個數(shù)組(集合),并使用 PHP 的內(nèi)部指針來跟蹤迭代。你還可以通過自己跟蹤集合索引來實現(xiàn)迭代器。這需要 Library 中的一種新的 accessor 方法來通過關鍵字訪問對象。
| class Library { // ... function get($key) { if (array_key_exists($key, $this->collection)) { return $this->collection[$key]; } } } |
同樣,在 Library::getIterator() 方法中,你可能將 $this(library 本身)傳遞給構造程序,而不是將 $this 傳遞給集合(數(shù)組包含Media 集合)。外部的迭代器然后只是內(nèi)部地跟蹤指針以了解它當前引用的是哪一個 Library 集合元素,并將使用構造行數(shù)中從引用到 Library 的傳遞來檢索當前的對象。
|
class LibraryGofExternalIterator { |
不同的迭代器 API
雖然前面的代碼是 GoF 所述迭代器模式的完整實現(xiàn),你還可能會發(fā)現(xiàn)四種方法的 API 有一點臃腫。如果是,你可以將 collapse next(), currentItem(), 和 isDone() 都并入 next() 中,用來從集合中返回本項或下一項,或者如果整個集合被遍歷過了,則返回 false。這是一個測試不同 API 的代碼:
|
class IteratorTestCase extends UnitTestCase { |
|
class Library { |
|
class LibraryIterator { |
請注意調(diào)試結果的紅色標記!什么導致發(fā)生錯誤“Equal expectation fails at character 4 with name1name2name3 and name2name3”?不知何故,跳過了第一次迭代 - 這是 bug。要修訂該錯誤,對于 next() 方法的第一次調(diào)用,返回 current()。
| class LibraryIterator { protected $collection; protected $first=true; function __construct($collection) { $this->collection = $collection; } function next() { if ($this->first) { $this->first = false; return current($this->collection); } return next($this->collection); } } |
Presto! 綠色條和改進的 while 循環(huán)迭代器。
過濾迭代器
利用迭代器,你不僅僅可以顯示集合中的每一項。你還可以選擇顯示的項。修改 Library::getIterator() 來使用其它兩種迭代器類型。
| class Library { // ... function getIterator($type=false) { switch (strtolower($type)) { case ‘media’: $iterator_class = ‘LibraryIterator’; break; case ‘available’: $iterator_class = ‘LibraryAvailableIterator’; break; case ‘released’: $iterator_class = ‘LibraryReleasedIterator’; break; default: $iterator_class = ‘LibraryGofIterator’; } return new $iterator_class($this->collection); } } |
類 LibraryAvailableIterator 僅可以迭代狀態(tài)為“library”的項”(重新調(diào)用 checkOut() 方法,將狀態(tài)更改為“borrowed”)。
|
class IteratorTestCase extends UnitTestCase { |
| class LibraryAvailableIterator { protected $collection = array(); protected $first=true; function __construct($collection) { $this->collection = $collection; } function next() { if ($this->first) { $this->first = false; $ret = current($this->collection); } else { $ret = next($this->collection); } if ($ret && ‘library’ != $ret->status) { return $this->next(); } return $ret; } } |
迭代器不僅可以顯示全部或部分集合。而且,還可以按特定順序顯示集合。下面,創(chuàng)建一個按集合眾介質(zhì)的發(fā)布日期進行排序的迭代器。為了進行測試,請?zhí)砑幽承┤掌谠?setUp() 方法中添加的項之后的介質(zhì)實例。如果迭代器運行,則這些日期較后的項應該位于迭代操作的最前面。
| class IteratorTestCase extends UnitTestCase { // ... function TestReleasedIteratorUsage() { $this->lib->add(new Media(‘second’, 1999)); $this->lib->add(new Media(‘first’, 1989)); $this->assertIsA( $it = $this->lib->getIterator(‘released’) ,’LibraryReleasedIterator’); $output = array(); while ($item = $it->next()) { $output[] = $item->name .’-’. $item->year; } $this->assertEqual( ‘first-1989 second-1999 name1-2000 name3-2001 name2-2002’ ,implode(‘ ‘,$output)); } } |
該測試使用的項在每個迭代中略有不同:并不僅僅是在字符串值后添加 $name,而是,字符串同時具有 $name 和 $year 屬性,這些屬性隨后將被添加到 $output 數(shù)組。LibraryReleasedIterator 的實現(xiàn)與 LibraryIterator 非常類似,除了 constuctor 中的一行語句:
| class LibraryReleasedIterator extends LibraryIterator { function __construct($collection) { usort($collection, create_function(‘$a,$b’,’ return ($a->year - $b->year);’)); $this->collection = $collection; } } |
用粗體表示的這一行將 $collection 數(shù)組排在迭代之前。你可以通過簡單地繼承 LibraryIterator 類,來避免復制該類的其它所有代碼�?梢允褂猛獠康鱽韺崿F(xiàn)相同的排序迭代嗎?是的,但是你必須注意完成它的訣竅。
分享:《PHP設計模式介紹》第六章 偽對象模式面向?qū)ο蟮木幊讨载S富多彩,部分是由于對象間的相互聯(lián)系與作用。一個單一的對象就能封裝一個復雜的子系統(tǒng),使那些很復雜的操作能夠通過一些方法的調(diào)用而簡化。(無所不在的數(shù)據(jù)庫連接就是這
- 相關鏈接:
- 教程說明:
PHP教程-《PHP設計模式介紹》第八章 迭代器模式(3)
。