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

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

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

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

class  LibraryReleasedExternalIterator  {
protected  $collection; protected  $sorted_keys; protected  $key=-1;
function  __construct($collection)  {
$this->collection  =  $collection;
$sort_funct  =  create_function(
í $a,$b,$c=false’,
í畇tatic  $collection;
if  ($c)  {
$collection  =  $c;
return;
}
return  ($collection->get($a)->year  -
$collection->get($b)->year);’);
$sort_funct(null,null,$this->collection);
$this->sorted_keys  =  $this->collection->keys();
usort($this->sorted_keys,  $sort_funct);
}
function  next()  {
if  (++$this->key  >=  $this->collection->count())  {
return  false;
}  else  {
return  $this->collection->get($this->sorted_keys[$this->key]);
}
}
}


其中,关键是创建用于排序的实用程序函数。排序函数必须能够访问集合,以便可以获取对照成员。然而,因为 gener- ated 函数在 usort() 中使用,没有将集合作为其它参数传递的选项。相反,你可以利用上述代码块中显示的诀窍,在利用 usort() 调用函数之前,将引用存储在函数中内部的集合中。排序的项是集合的关键字列表。当 usort() 完成时,关键字会按照集合中每个对象的 year 属性的顺序进行排序。在 next() 方法中,可以通过 get() 方法访问集合中的对象,而不是间接通过 $sorted_keys 映射。如果重新调用外部版本的 GoF 风格的迭代器,则不连续的数组或关键字中的字符串可能会有问题。可以使用针对 sim- ple 外部迭代器的相同诀窍,来减少关键字顺序不连贯的问题。

 

SPL 迭代器

     《迭代器设计模式和 PHP》中必须论述“标准 PHP 库”(SPL)迭代器。虽然,使用 while 循环结构可以非常紧凑,并且也很有用,但是 PHP 代码或许更适合数组迭代的 foreach 结构。直接在 foreach 循环中使用集合合适吗?这其实就是 SPL 迭代器的目标。(尽管本章整篇都是写 PHP5,下列 SPL 代码只能在 PHP5 中运行,并且仅当在 PHP5 编译中将 SPL 启用。)

Fuecks 写过一篇文章,详细地介绍了 SPL 和 SPL 迭代器;请参阅 http://www.site-

point.com/article/php5-standard-library。使用 SPL 是一种完全不同的实现迭代的方法,因此首先介绍一个新单元测试例子和一个新的类 ForeachableLibrary。

class  SplIteratorTestCase  extends  UnitTestCase  {
protected  $lib;
function  setup()  {
$this->lib  =  new  ForeachableLibrary;
$this->lib->add(new  Media(‘name1’,  2000));
$this->lib->add(new  Media(‘name2’,  2002));
$this->lib->add(new  Media(‘name3’,  2001));
}
function  TestForeach()  {
$output  = ‘’;
foreach($this->lib  as  $item)  {
$output  .=  $item->name;
}
$this->assertEqual(‘name1name2name3’,  $output);
}
}

ForeachableLibrary 是实现 SPL 迭代器接口的集合。你必须执行 5 个函数来创建 SPL 迭代器:current()、next()、key()、valid() 和 rewind()。 key() 返回集合的当前索引。 rewind() 类似于 reset():在集合启动时重新启动迭代。

class  ForeachableLibrary
extends  Library implements  Iterator  {
protected  $valid;
function  current()  {
return  current($this->collection);
}
function  next()  {
$this->valid  =  (false  !==  next($this->collection));
}
function  key()  {
return  key($this->collection);
}
function  valid()  {
return  $this->valid;
}
function  rewind()  {
$this->valid  =  (false  !==  reset($this->collection));
}
}

这里,该代码仅仅实现了处理 $collection 属性的必需的函数。(如果你没有实现所有 5 个函数,并且将实现迭代器添加到类 definition,则 PHP 将出现致命错误。)测试尚不成熟,因此,什么都有可能发生。存在一个问题:事实受限于一种迭代类型 - 排序,或者 fil- tering 不可用。可以采取措施来调整这种情况?是的!应用从策略模式中学到的知识(请参阅第 7 章),将 SPL 迭代器的 5 个函数作为另一个对象的示例。这是关于 PolymorphicForeachableLibrary 的测试。

class  PolySplIteratorTestCase  extends  UnitTestCase  {
protected  $lib;
function  setup()  {
$this->lib  =  new  PolymorphicForeachableLibrary;
$this->lib->add(new  Media(‘name1’,  2000));
$this->lib->add(new  Media(‘name2’,  2002));
$this->lib->add(new  Media(‘name3’,  2001));
}
function  TestForeach()  {
$output  = ‘’;
foreach($this->lib  as  $item)  {
$output  .=  $item->name;
}
$this->assertEqual(‘name1name2name3’,  $output);

}
}

这种情况与 SplIteratorTestCase 测试的唯一差别在于 $this->lib 属性类是在 setUp() 方法中创建的。这意味着:这两个类的运行方式必须一致。PolymorphicForeachableLibrary:class  PolymorphicForeachableLibrary扩展库

implements  Iterator  {
protected  $iterator;
function  current()  {
return  $this->iterator->current();
}
function  next()  {
return  $this->iterator->next();
}
function  key()  {
return  $this->iterator->key();
}
function  valid()  {
return  $this->iterator->valid();
}
function  rewind()  {
$this->iterator  =
new  StandardLibraryIterator($this->collection);
$this->iterator->rewind();
}
}

扩展库加入集合处理方法。并添加 SPL 方法,这些方法代表了 $iterator 属性,在 rewind() 中创建。以下是StandardLibraryIterator 的代码。

 

class  StandardLibraryIterator  {
protected  $valid;
protected  $collection;
function  __construct($collection)  {
$this->collection  =  $collection;
}
function  current()  {
return  current($this->collection);
}
function  next()  {
$this->valid  =  (false  !==  next($this->collection));
}
function  key()  {
return  key($this->collection);
}
function  valid()  {

return  $this->valid;
}
function  rewind()  {
$this->valid  =  (false  !==  reset($this->collection));
}
}

该代码看起来很熟悉:实际上,这来自于 5 个 SPL 函数ForeachableLibrary 类。

测试类

现在,代码更加复杂了,但是其如何支持其它迭代器类型?添加一个关于“发行版”迭代器的测试,来查看这种设计的其它迭代器如何工作。

class  PolySplIteratorTestCase  extends  UnitTestCase  {
//  ...
function  TestReleasedForeach()  {
$this->lib->add(new  Media(‘second’,  1999));
$this->lib->add(new  Media(‘first’,  1989));
$output  =  array();
$this->lib->iteratorType(‘Released’);
foreach($this->lib  as  $item)  {
$output[]  =  $item->name  .’-’. $item->year;
}
$this->assertEqual(
‘first-1989  second-1999  name1-2000  name3-2001  name2-2002’
,implode(‘  ‘,$output));
}
}

上面的测试用例看起来也很熟悉,因为其非常类似于前一个“发行版”迭代器,但是

使用了 foreach 控制结构进行循环。

class  PolymorphicForeachableLibrary
extends  Library implements  Iterator  {
protected  $iterator_type;
protected  $iterator;
function  __construct()  {
$this->iteratorType();
}
function  iteratorType($type=false)  {
switch(strtolower($type))  {
case  ‘released’:
$this->iterator_type  =  ‘ReleasedLibraryIterator’;
break;
default:
$this->iterator_type  =  ‘StandardLibraryIterator’;
}
$this->rewind();
}
//  ...
function  rewind()  {
$type  =  $this->iterator_type;
$this->iterator  =  new  $type($this->collection);
$this->iterator->rewind();
}
}


新的 iteratorType() 方法使你转变要使用的迭代器的类型。(因为迭代器类型并不是在对象安装期间选中的,并且你可以在空闲时再次调用 iteratorType() 方法来选择不同迭代器类型,所以实际上是在 State 模式执行代码,而不是 Strategy 模式。)

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

你可以简单地通过扩展 StandardLibraryIterator 并覆盖构造函数来添加入局数组的排序,从而实现 ReleasedLibraryIterator。并且,通过它,你可以有一个 working PolymorphicForeachableLibrary。

总结

迭代器是标准化地地处理应用程序中对象集合的方法。这些例子是基于数组的,但是对于拥有同一个接口的非数组集合,工作起来将更加强大。使用 foreach 控制结构方式的集合确实非常酷。 SPL 实现中最不幸的问题是与迭代器可能存在的名称空间冲突。有多少 PHP4 面向对象的代码拥有类似于迭代器类作为库迭代器类的基类?在一些容量中有多少 5 种必需方法的定义?可能一个更加具有深刻含义的名称就能实现 Foreachable。如果你选择使用 SPL,则还应该研究其它支持的迭代器,例如RecursiveArrayIterator 和其它众多迭代器。


延伸阅读:
从魔兽看PHP设计模式
《PHP设计模式介绍》导言
PHP设计模式介绍 第一章 编程惯用法
PHP设计模式介绍 第二章 值对象模式
PHP设计模式介绍 第三章 工厂模式
PHP设计模式介绍 第四章 单件模式
PHP设计模式介绍 第五章 注册模式
PHP设计模式介绍 第六章 伪对象模式
PHP设计模式介绍 第七章 策略模式
最新文章
推荐阅读
月点击排行榜
PHP程序员站 Copyright © 2007-2010,PHPERZ.COM All Rights Reserved 粤ICP备07503606号