php 不常用设计模式-迭代器模式(迭代器)
“英雄不问出处”,首先你要问下自己是不是英雄。
迭代器是PHP的一种设计模式,也叫迭代器模式
PHP5 开始内置了 Iterator 即迭代器接口,所以如果你定义了一个类,并实现了 Iterator 接口,那么你的这个类对象就是 ZEND_ITER_OBJECT 即可迭代的,否则就是 ZEND_ITER_PLAIN_OBJECT
Iterator 底层类
interface Iterator extends Traversable {
// 获取当前内部标量指向的元素的数据
public mixed current()
// 获取当前标量
public scalar key()
// 移动到下一个标量
public void next()
// 重置标量
public void rewind()
// 检查当前标量是否有效
public boolean valid()
}
ZEND_ITER_PLAIN_OBJECT 类型的类
常规实现 range 函数:
1、range — 根据范围创建数组,包含指定的元素
2、array range (mixed start , mixed end [, number $step = 1 ])
3、建立一个包含指定范围单元的数组。
function range ($start, $end, $step = 1){
$ret = [];
for ($i = $start; $i <= $end; $i += $step) {
$ret[] = $i;
}
return $ret;
}
迭代器实现的 xrange函数
class Xrange implements Iterator {
protected $start;
protected $limit;
protected $step;
protected $current;
public function __construct($start, $limit, $step = 1) {
$this->start = $start;
$this->limit = $limit;
$this->step = $step;
}
public function rewind() {
$this->current = $this->start;
}
public function next() {
$this->current += $this->step;
}
public function current() {
return $this->current;
}
public function key() {
return $this->current + 1;
}
public function valid() {
return $this->current <= $this->limit;
}
}
调用
foreach (new Xrange(0, 9) as $key => $val) {
echo $key, ' ', $val, "\n";
}
看上去功能和 range() 函数所做的一致,不同点在于迭代的是一个 对象(Object) 而不是数组:同时,占用内存情况也不相同,迭代器占用的很少。
在 yii 框架中就使用了 迭代器 batch()
$query = (new \yii\db\Query)->from('user');
// yii\db\BatchQueryResult
foreach ($query->batch() as $users) {
// 每次循环得到多条 user 记录
}
优点:
- 支持多种遍历方式。比如有序列表,我们根据需要提供正序遍历、倒序遍历两种迭代器。用户只需要得到我们的迭代器,就可以对集合执行遍历操作
- 简化了聚合类。由于引入了迭代器,原有的集合对象不需要自行遍历集合元素了
- 增加新的聚合类和迭代器类很方便,两个维度上可各自独立变化
- 为不同的集合结构提供一个统一的接口,从而支持同样的算法在不同的集合结构上操作
缺点:
- 迭代器模式将存储数据和遍历数据的职责分离增加新的集合对象时需要增加对应的迭代器类,类的个数成对增加,在一定程度上增加系统复杂度。
使用场景
- 使用返回迭代器的包或库时(如 PHP5 中的 SPL 迭代器)
- 无法在一次调用获取所需的所有元素时
- 要处理数量巨大的元素时(数据库中要处理的结果集内容超过内存)