-
以前也看过迭代器Iterator接口,感觉不如yied好用,因此实际工作中并没有用到过。
-
今天看了一篇网上的博客(https://www.cnblogs.com/wwjchina/p/7723499.html),想起自己之前看过的迭代器Iterator,好像也是这么讲的,然而看完后,发现好像更迷糊了。
-
下面我说一下该博客讲解的内容,让我迷惑的地方
1、php提供了一个语法结构用于遍历数组和对象 foreach
2、而foreach则不能直接遍历对象里面的属性,需要通过迭代器(预定义接口 Iterator)
-
先来看这两条,首先看第1条,
- foreach可以遍历数组和对象。遍历数组大家都知道,foreach遍历对象可能就会有疑问了。
- 刚开始我以为遍历对象是将对象中所有的属性和方法都遍历出来,后来发现不是。
- foreach遍历对象时,仅遍历对象属性。对象的属性是一个集合数组, 因此foreach遍历对象时就是遍历对象中所有的公开属性,private遍历不到。
-
再来看第2条,表述有歧义,
- foreach则不能直接遍历对象里面的属性,这句话中对象里的属性是指单个属性,还是指所有属性?
- 经过对第一条的分析,我们知道foreach可以遍历对象中所有公开的属性,当然public属性是不合理的,这里我们只考虑实现可能性。
- 然后我又尝试了一下,foreach遍历对象中指定的单个属性(public),也没问题,此处可以通过一个public方法获得该属性,从而将属性设置为private。
-
看一下该博客中给出的示例代码, 实现了Iterator 接口中的5个抽象方法。
class Season implements Iterator{
private $position = 0;//指针指向0
private $arr = array('春','夏','秋','冬');
public function rewind(){
return $this -> position = 0;
}
public function current(){
return $this -> arr[$this -> position];
}
public function key(){
return $this -> position;
}
public function next() {
++$this -> position;
}
public function valid() {
return isset($this -> arr[$this -> position]);
}
}
$obj = new Season;
foreach ($obj as $key => $value) {
echo $key.':'.$value."
";
}
?>
结果:
0:春
1:夏
2:秋
3:冬
- 看了这个例子,我更迷惑了,实现5个方法,然后遍历一个对象中的一个属性,为什么不知通过一个getAttr方法返回该属性,直接遍历呢?
- 到此,该博客就完了,我的迷惑还未消除,但我注意到了,使用迭代器进行foreach遍历时,并没有显式的调用 Iterator的五个方法。
- 于是刚刚好,看到了另一篇博客 https://blog.csdn.net/luyaran/article/details/82867878,专门讲解迭代器模式的,其中这两句话讲出了foreach遍历对象和使用迭代器遍历对象的区别,
PHP5开始支持了接口, 并且内置了Iterator接口, 所以如果你定义了一个类,并实现了Iterator接口,那么你的这个类对象就是ZEND_ITER_OBJECT,否则就是ZEND_ITER_PLAIN_OBJECT。
对于ZEND_ITER_PLAIN_OBJECT的类,foreach会通过HASH_OF获取该对象的默认属性数组,然后对该数组进行foreach,而对于ZEND_ITER_OBJECT的类对象,则会通过调用对象实现的Iterator接口相关函数来进行foreach。
- 下面这句话是迭代器的定义,字字玑珠, 细细品味
来看下迭代器的定义,那就是提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部显示。它可帮助构造特定的对象,那些对象能够提供单一标准接口循环或迭代任何类型的可计数数据
- 再来看看迭代器内部的五个方法
Iterator::current — Return the current element 返回当前元素
Iterator::key — Return the key of the current element 返回当前元素的键
Iterator::next — Move forward to next element 移向下一个元素
Iterator::rewind — Rewind the Iterator to the first element 重新回到第一个元素
Iterator::valid — Checks if current position is valid 检查当前位置的有效性
- 看过了第一位博主的例子, 下面再看看第二位博主给出的实例
class MyIterator implements Iterator
{
private $var = array();
public function __construct($array)
{
if (is_array($array)) {
$this->var = $array;
}
}
public function rewind() {
echo "倒回第一个元素
";
reset($this->var);
}
public function current() {
$var = current($this->var);
echo "当前元素: $var
";
return $var;
}
public function key() {
$var = key($this->var);
echo "当前元素的键: $var
";
return $var;
}
public function next() {
$var = next($this->var);
echo "移向下一个元素: $var
";
return $var;
}
public function valid() {
$var = $this->current() !== false;
echo "检查有效性: {$var}
";
return $var;
}
}
$values = array(1,2,3);
$it = new MyIterator($values);
foreach ($it as $k => $v) {
print "此时键值对 -- key $k: value $v
";
}
-
在这里,我们向实现迭代器接口的类中传入了一个数组变量,生成了一个对象,专门用来遍历。
-
我们还可以在类的内部定义一些其他的操作,但从设计模式来讲,最好分开,不要违背单一职责原则,根据场景进行区分。
-
这样可以向我们的迭代器中传入不同的数组进行遍历,
-
同时当我们想换一种遍历方式时,只需另外建立一个迭代器,根据需求(比如降序遍历)重新实现5个方法的代码就行了。
-
数据和算法分离,独立发展
-
附上第二位博主的总结
来看下迭代器的优点:
1.支持多种遍历方式。比如有序列表,我们根据需要提供正序遍历、倒序遍历两种迭代器。用户只需要得到我们的迭代器,就可以对集合执行遍历操作
2.简化了聚合类。由于引入了迭代器,原有的集合对象不需要自行遍历集合元素了
3.增加新的聚合类和迭代器类很方便,两个维度上可各自独立变化
4.为不同的集合结构提供一个统一的接口,从而支持同样的算法在不同的集合结构上操作
缺点就是迭代器模式将存储数据和遍历数据的职责分离增加新的集合对象时需要增加对应的迭代器类,类的个数成对增加,在一定程度上增加系统复杂度。
它的使用场景,我们可以参考如下几点:
1.访问一个聚合对象内容而无须暴露它的内部显示
2.需要为聚合对象提供多种遍历方式
3.为遍历不同的聚合结构提供一个统一的接口
最后,咱们来看一个网上找的用迭代器模式来实现的一个斐波那契数列。
我们都知道,斐波那契数列通常做法是用递归实现,当然还有其它的方法,咱们这里用PHP的迭代器来实现一个斐波纳契数列,几乎没有什么难度,只是把类里的next()方法重写了一次。注释已经写到代码中,也是相当好理解的,如下:
class Fibonacci implements Iterator {
private $previous = 1;
private $current = 0;
private $key = 0;
public function current() {
return $this->current;
}
public function key() {
return $this->key;
}
public function next() {
// 关键在这里
// 将当前值保存到 $newprevious
$newprevious = $this->current;
// 将上一个值与当前值的和赋给当前值
$this->current += $this->previous;
// 前一个当前值赋给上一个值
$this->previous = $newprevious;
$this->key++;
}
public function rewind() {
$this->previous = 1;
$this->current = 0;
$this->key = 0;
}
public function valid() {
return true;
}
}
$seq = new Fibonacci;
$i = 0;
foreach ($seq as $f) {
echo "$f ";
if ($i++ === 15) break;
}
输出的结果如下:
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610
再次附上原文地址
1:https://www.cnblogs.com/wwjchina/p/7723499.html
2:https://blog.csdn.net/luyaran/article/details/82867878