PHP程序员站--PHP编程开发平台
 当前位置:主页 >> PHP高级编程 >> 高级应用 >> 

PHP设计模式介绍 第八章 迭代器模式

PHP设计模式介绍 第八章 迭代器模式

来源:互联网  作者:  发布时间:2010-05-21
类中的面向对象编程封装应用逻辑。类,就是实例化的对象,每个

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 开始建立索引,并且是完全连续的。

不同的迭代器 API

虽然前面的代码是 GoF 所述迭代器模式的完整实现,你还可能会发现四种方法的 API 有一点臃肿。如果是,你可以将 collapse next(), currentItem(), 和 isDone() 都并入 next() 中,用来从集合中返回本项或下一项,或者如果整个集合被遍历过了,则返回 false。这是一个测试不同 API 的代码:

class  IteratorTestCase  extends  UnitTestCase  {
//  ...
function  TestMediaIteratorUsage()  {
$this->assertIsA(
$it  =  $this->lib->getIterator(‘media’)
,’LibraryIterator’);
$output  =  ‘’;
while  ($item  =  $it->next())  {
$output  .=  $item->name;
}
$this->assertEqual(‘name1name2name3’,  $output);
}
}

在上述代码中,注意简化的循环控制结构。 next() 返回对象或者false,允许你在 while 循环条件中执行分配。下面的一些示例检验使用较小接口的不同迭代器模式。为了方便,将 Library::getIterator() 方法更改为参数化的 Factory,以便你可以从单一方法中获取四种的方法迭代器或两种方法的迭代器(next() 和 reset())。

class  Library  {
//  ...
function  getIterator($type=false)  {
switch  (strtolower($type))  {
case  ‘media’:
$iterator_class  =  ‘LibraryIterator’;
break;
default:
$iterator_class  =  ‘LibraryGofIterator’;
}
return  new  $iterator_class($this->collection);
}
}

这里面的 Library::getIterator() 现在接受一个参数以选择返回什么样的迭代器。缺省为 LibraryGofIterator(因此现有的测试仍然能够通过)。将字符串媒体传递给所创建的方法,并返回 LibraryIterator。这是一些实现 LibraryIterator 的代码:

class  LibraryIterator  {
protected  $collection;
function  __construct($collection)  {
$this->collection  =  $collection;
}
function  next()  {
return  next($this->collection);
}
}


请注意调试结果的红色标记!什么导致发生错误“Equal expectation fails at character 4 with name1name2name3 and name2name3”?不知何故,跳过了第一次迭代 - 这是 bug。要修订该错误,对于 next() 方法的第一次调用,返回 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 循环迭代器。

 过滤迭代器

利用迭代器,你不仅仅可以显示集合中的每一项。你还可以选择显示的项。修改 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 仅可以迭代状态为“library”的项”(重新调用 checkOut() 方法,将状态更改为“borrowed”)。

class  IteratorTestCase  extends  UnitTestCase  {
//  ...
function  TestAvailableIteratorUsage()  {
$this->lib->add($dvd  =  new  Media(‘test’,  1999));
$this->lib->add(new  Media(‘name4’,  1999));
$this->assertIsA(
$it  =  $this->lib->getIterator(‘available’)
,’LibraryAvailableIterator’);
$output  = ‘’;
while  ($item  =  $it->next())  {
$output  .=  $item->name;
}
$this->assertEqual(‘name1name2name3testname4’,  $output);
$dvd->checkOut(‘Jason’);
$it  =  $this->lib->getIterator(‘available’);
$output  = ‘’;
while  ($item  =  $it->next())  {
$output  .=  $item->name;
}
$this->assertEqual(‘name1name2name3name4’,  $output);
}
}

该测试创建一个新的介质实例,并将其存储在变量 $dvd 中。突出显示第一个 assertEqual() 声明验证利用 LibraryAvailableIterator 进行迭代时,存在一个新的项。接下来,测试使用 checkOut() 方法,并验证新的项已丢失,不显示。实现过滤得代码非常类似于 LibraryIterator::next(),差别在于在返回项之前执行过滤。如果当前项与过滤条件不匹配,则代码返回 $this->next()。

 

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;
}
}
排序迭代器

迭代器不仅可以显示全部或部分集合。而且,还可以按特定顺序显示集合。下面,创建一个按集合众介质的发布日期进行排序的迭代器。为了进行测试,请添加某些日期在 setUp() 方法中添加的项之后的介质实例。如果迭代器运行,则这些日期较后的项应该位于迭代操作的最前面。

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 数组。LibraryReleasedIterator 的实现与 LibraryIterator 非常类似,除了 constuctor 中的一行语句:

class  LibraryReleasedIterator  extends  LibraryIterator  {
function  __construct($collection)  {
usort($collection,  create_function(‘$a,$b’,’ return  ($a->year  -  $b->year);’));
$this->collection  =  $collection;
}
}

用粗体表示的这一行将 $collection 数组排在迭代之前。你可以通过简单地继承 LibraryIterator 类,来避免复制该类的其它所有代码。可以使用外部迭代器来实现相同的排序迭代吗?是的,但是你必须注意完成它的诀窍。


延伸阅读:
从魔兽看PHP设计模式
《PHP设计模式介绍》导言
PHP设计模式介绍 第一章 编程惯用法
PHP设计模式介绍 第二章 值对象模式
PHP设计模式介绍 第三章 工厂模式
PHP设计模式介绍 第四章 单件模式
PHP设计模式介绍 第五章 注册模式
PHP设计模式介绍 第六章 伪对象模式
PHP设计模式介绍 第七章 策略模式

最新文章
推荐阅读
月点击排行榜
PHP程序员站 Copyright © 2007-2010,PHPERZ.COM All Rights Reserved 粤ICP备07503606号