《PHP设计模式介绍》第八章 迭代器模式(2)_PHP教程
推荐:《PHP设计模式介绍》第七章 策略模式在编写面向对象的代码的时,有些时候你需要一个能够自己根据不同的条件来引入不同的操作对象实例。例如,一个菜单功能能够根据用户的“皮肤”首选项来决定是否采用水平的还是垂直的排
你的集合类必须提供 Factory(参见第 3 章)来创建迭代器的实例。
迭代器类定义 first() 转到集合开始的接口,
next() 移到序列中的下一个项作为你的循环,currentItem() 从集合检索当前的项作为你的循环, isDone() 用于指出你在整个集合中循环结束的时间。
在“示例代码”部分,LibraryGofIterator 类是一个直接实现 GoF 迭代器设计模式的示例。
样本代码
在 Library 内实现 GoF 迭代器模式的第一步是为新的具体迭代器写一个新的测试用例。因为每一种测试方法都将操纵包含 Media 实例的 Library,你可以清空 UnitTestCase::setUp() 方法,从而在每种测试的已知状态下将变量填充到 Library 中。
首先,将 Library::getIterator() 方法作为LibraryGofIterator 类的 一个 Factory 实例。
class IteratorTestCase extends UnitTestCase { protected $lib; function setup() { $this->lib = new Library; $this->lib->add(new Media(‘name1’, 2000)); $this->lib->add(new Media(‘name2’, 2002)); $this->lib->add(new Media(‘name3’, 2001)); } function TestGetGofIterator() { $this->assertIsA($it = $this->lib->getIterator() ,’LibraryGofIterator’); } } |
实现:
class Library { // ... function getIterator() { return new LibraryGofIterator($this->collection); } } |
getIterator() 方法将 Library 的 $collection 传递给新的具体迭代器结构。这一方法有两个重要的实现:每个迭代器都是独立的,因此可以同时操作多个迭代器。另外,迭代器在数组上的操作是当迭代器被请求时才执行的。如果之后将另一个项添加到集合中,你必须请求另一个迭代器来显示它(至少是在该实现中)。让我们通过将声明添加到 TestGetGofIterator() 方法以匹配迭代器设计模式,继续对测试进行加强。
如果你已经对整个集合进行遍历,则 isDone() 方法只应该为 true。如果 iterator 刚刚创建,则 isDone() 显然返回 false,从而指出集合可以遍历。
class IteratorTestCase extends UnitTestCase { function setup() { /* ... */ } function TestGetGofIterator() { $this->assertIsA($it = $this->lib->getIterator() ,’LibraryGofIterator’); $this->assertFalse($it->isdone()); } } |
与 TDD 一样,尽可能实现最简单的代码来满足你的测试用例:
class LibraryGofIterator { function isDone() { return false; } } |
因此,在第一个迭代器间,应该发生什么呢? currentItem() 应该返回第一个 Media 对象,这个对象是在 IteratorTestCase::setUp() 方法中添加的,isDone() 应该继续为 false,因为另两个项仍然等待遍历。
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()); } } |
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); } } |
迭代器测试用例只存在一个问题:它没有反映迭代器的典型用法。是的,它测试了迭代器模式的所有功能,但应用程序需要采用更简单的方法来使用迭代器。因此,下一步是使用更贴实际的代码来编写测试。
分享:《PHP设计模式介绍》第六章 伪对象模式面向对象的编程之所以丰富多彩,部分是由于对象间的相互联系与作用。一个单一的对象就能封装一个复杂的子系统,使那些很复杂的操作能够通过一些方法的调用而简化。(无所不在的数据库连接就是这
- 相关链接:
- 教程说明:
PHP教程-《PHP设计模式介绍》第八章 迭代器模式(2)。