php教程:php设计模式介绍之迭代器模式(3)
LibraryGofIterator 接收了构造函数中的 $collection, 这一点非常重要(参见上面的 Library 最小化实现)并从 currentItem() 方法返回 current() 项。
class LibraryGofIterator {
protected $collection;
function __construct($collection) {
$this->collection = $collection;
}
function currentItem() {
return current($this->collection);
}
function isDone() {
return false;
}
}
在下一个迭代会出现什么? next() 方法应该更改currentItem() 方法返回的项。下面的测试捕获了所期望的行为:
class IteratorTestCase extends UnitTestCase {
function setup() { /* ... */ }
function TestGetGofIterator() {
$this->assertIsA($it = $this->lib->getIterator(), ‘LibraryGofIterator’);
$this->assertFalse($it->isdone());
$this->assertIsA($first = $it->currentItem(), ‘Media’);
$this->assertEqual(‘name1’, $first->name);
$this->assertFalse($it->isdone());
$this->assertTrue($it->next());
$this->assertIsA($second = $it->currentItem(), ‘Media’);
$this->assertEqual(‘name2’, $second->name);
$this->assertFalse($it->isdone());
}
}
重新建立在 PHP 的数组函数之上,在数组上使用 next():
class LibraryGofIterator {
protected $collection;
function __construct($collection) {
$this->collection = $collection;
}
function currentItem() {
return current($this->collection);
}
function next() {
return next($this->collection);
}
function isDone() {
return false;
}
}
除了 isDone() 方法必须返回 之外,第三个迭代看起来很像其他的迭代。你还希望 next() 能够成功移到下一个迭代:
class IteratorTestCase extends UnitTestCase {
function setup() { /* ... */ }
function TestGetGofIterator() {
$this->assertIsA($it = $this->lib->getIterator(), ‘LibraryGofIterator’);
$this->assertFalse($it->isdone());
$this->assertIsA($first = $it->currentItem(), ‘Media’);
$this->assertEqual(‘name1’, $first->name);
$this->assertFalse($it->isdone());
$this->assertTrue($it->next());
$this->assertIsA($second = $it->currentItem(), ‘Media’);
$this->assertEqual(‘name2’, $second->name);
$this->assertFalse($it->isdone());
$this->assertTrue($it->next());
$this->assertIsA($third = $it->currentItem(), ‘Media’);
$this->assertEqual(‘name3’, $third->name);
$this->assertFalse($it->next());
$this->assertTrue($it->isdone());
}
}
对 next() 和 isDone() 方法稍加修改,所有的测试都通过了。代码如下:
class LibraryGofIterator {
protected $collection;
function __construct($collection) {
$this->collection = $collection;
}
function first() {
reset($this->collection);
}
function next() {
return (false !== next($this->collection));
}
function isDone() {
return (false === current($this->collection));
}
function currentItem() {
return current($this->collection);
}
}
迭代器测试用例只存在一个问题:它没有反映迭代器的典型用法。是的,它测试了迭代器模式的所有功能,但应用程序需要采用更简单的方法来使用迭代器。因此,下一步是使用更贴实际的代码来编写测试。
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);
}
}
目前,迭代器的实现复制了某个数组(集合),并使用 PHP 的内部指针来跟踪迭代。你还可以通过自己跟踪集合索引来实现迭代器。这需要 Library 中的一种新的 accessor 方法来通过关键字访问对象。
class Library {
// ...
function get($key) {
if (array_key_exists($key, $this->collection)) {
return $this->collection[$key];
}
}
}
同样,在 Library::getIterator() 方法中,你可能将 $this(library 本身)传递给构造程序,而不是将 $this 传递给集合(数组包含Media 集合)。外部的迭代器然后只是内部地跟踪指针以了解它当前引用的是哪一个 Library 集合元素,并将使用构造行数中从引用到 Library 的传递来检索当前的对象。
class LibraryGofExternalIterator {
protected $key = 0;
protected $collection;
function __construct($collection) {
$this->collection = $collection;
}
function first() {
$this->key=0;
}
function next() {
return (++$this->key < $this->collection->count());
}
function isDone() {
return ($this->key >= $this->collection->count());
}
function currentItem() {
return $this->collection->get($this->key);
}
}
这一实现假设你的集合数组从 0 开始建立索引,并且是完全连续的。