SPL迭代器接口(五)—OuterIterator Interface

  OuterIterator Interface: 继承自Iterator接口,允许将多个迭代器包裹其中。

  OuterIterator接口继承自Iterator接口。实现OuterIterator接口的迭代器类,允许将一个或多个迭代器包裹其中,并按顺序访问这些迭代器。

  OuterIterator比Iterator多了一个getInnerIterator方法,该方法根据定义的返回值类型,可以知道应该返回当前正在访问的迭代器。

  OuterIterator与IteratorAggregate不一样,foreach的时候PHP是不会自动调用getInnerIterator方法的,我刚开始实现这个接口的时候就是以为foreach会自动调用。以为直接返回当前指向的迭代器,就能想IteratorAggregate那样,把迭代器遍历出来。

  但是,实际上并不行。网上这方面的资料比较少。我参考了PHP源码里的内置的IteratorIterator迭代器和AppendIterator迭代器,实现了OuterIterator接口。代码如下,欢迎大家交流指正。
代码示例:

‹?php
//自定义MyOuterIterator实现OuterIterator接口
class MyOuterIterator implements OuterIterator{
    protected $iterators = array();  //存放迭代器
    protected $index;         //存放当前迭代器指针
    //构造方法,接收外部传入的迭代器
    public function __construct(Array $iterators){
        $this->iterators = $iterators;
    }
    //返回当前正在访问的迭代器
    public function getInnerIterator(){
        return $this->iterators[$this->index];
    }
    //返回当前指针指向数据
    public function current()
    {   
        echo __METHOD__,PHP_EOL;
        //返回当前指针指向MyIterator迭代器的指针,指向数据
        return $this->getInnerIterator()->current();
    }
    //数据指针+1
    public function next()
    {   
        echo __METHOD__,PHP_EOL;
        //当前指针指向MyIterator迭代器的指针,即数据指针+1
        $this->getInnerIterator()->next();
        //判断该数据指针是否越界,如果越界,则把迭代器指针下一个MyIterator迭代器。
        if(!$this->getInnerIterator()->valid()){
            //不判断是否小于count($this->iterators)的话,自增若越界会引起MyIterator初始化指针报错
            ++$this->index ‹ count($this->iterators)  && $this->getInnerIterator()->rewind();
        }
    }
    //验证指针是否越界
    public function valid()
    {   
        echo __METHOD__,PHP_EOL;
        return  $this->index ‹ count($this->iterators);
    }
    //重置指针
    public function rewind()
    {   
        $this->index = 0;
        //初始化MyIterator迭代器指针
        $this->getInnerIterator()->rewind();
    }
    //返回当前指针
    public function key()
    {   
        echo __METHOD__,PHP_EOL;
        //获取MyIterator指针
        $key = $this->getInnerIterator()->key();
        //为避免不同MyIterator迭代器实例有相同的key值,拼上前缀(迭代器存放位置下标)
        return $this->index.'-'.$key;
    }
}

//自定义的MyIterator类
class MyIterator implements Iterator
{
    protected $data = array();  //存放数据
    protected $index=0 ;         //存放当前指针
    //构造方法接收外部传入数据
    public function __construct(Array $data)
    {   
        $this->data = $data;
    }
    //返回当前指针指向数据
    public function current()
    {   
        echo __METHOD__,PHP_EOL;
        return $this->data[$this->index];
    }
    //指针+1
    public function next()
    {   
        echo __METHOD__,PHP_EOL;
        $this->index ++;
    }
    //验证指针是否越界
    public function valid()
    {   
        echo __METHOD__,PHP_EOL;
        return $this->index ‹ count($this->data);
    }
    //重置指针
    public function rewind()
    {   
        echo __METHOD__,PHP_EOL;
        $this->index = 0;
    }
    //返回当前指针
    public function key()
    {   
        echo __METHOD__,PHP_EOL;
        return $this->index;
    }
}

$arr1=array(1,2);
$arr2=array(3,4);
$iterators = array(new MyIterator($arr1),new MyIterator($arr2));
//实例化一个MyOuterIterator迭代器
$container = new MyOuterIterator($iterators);
//使用MyOuterIterator迭代器
foreach($container as $key => $val){
    echo $key.'=>'.$val.PHP_EOL;
}

  运行结果:

  遗憾地是,上面的代码可以顺序访问不同类型迭代器,但局限于这些迭代器都是实现Iterator接口的迭代器。不能访问实现IteratorAggregate接口的迭代器。

  下面是实现方式可以顺序访问实现IteratorAggregate接口的不同类型迭代器,同样遗憾地是,它不支持实现Iterator接口的迭代器。

  代码示例:

//自定义MyOuterIterator实现OuterIterator接口
class MyOuterIterator implements OuterIterator{
    protected $iterators = array();  //存放迭代器
    protected $index;         //存放当前迭代器指针
    protected $iterator;        //存放当前MyIteratorAggregate生成的MyIterator迭代器
    //构造方法,接收外部传入的迭代器
    public function __construct(Array $iterators){
        $this->iterators = $iterators;
    }
    //返回当前正在访问的迭代器
    public function getInnerIterator(){
        return $this->iterator;
    }
    //返回当前指针指向数据
    public function current()
    {   
        echo __METHOD__,PHP_EOL;
        //返回当前指针指向MyIterator迭代器的指针,指向数据
        return $this->getInnerIterator()->current();
    }
    //数据指针+1
    public function next()
    {   
        echo __METHOD__,PHP_EOL;
        //当前指针指向MyIterator迭代器的指针,即数据指针+1
        $this->getInnerIterator()->next();
        //判断该数据指针是否越界,如果越界,则把迭代器指针下一个MyIteratorAggregate迭代器。
        if(!$this->getInnerIterator()->valid()){
            //不判断是否小于count($this->iterators)的话,自增若越界会引起MyIterator初始化指针报错
            if(++$this->index ‹ count($this->iterators)){
                $this->iterator = $this->iterators[$this->index]->getIterator();
                $this->iterator->rewind();
            }
        }
    }
    //验证指针是否越界
    public function valid()
    {   
        echo __METHOD__,PHP_EOL;
        return  $this->index ‹ count($this->iterators);
    }
    //重置指针
    public function rewind()
    {   
        $this->index = 0;
        $this->iterator = $this->iterators[$this->index]->getIterator();
        //初始化MyIterator迭代器指针
        $this->getInnerIterator()->rewind();
    }
    //返回当前指针
    public function key()
    {   
        echo __METHOD__,PHP_EOL;
        //获取MyIterator指针
        $key = $this->getInnerIterator()->key();
        //为避免不同MyIterator迭代器实例有相同的key值,拼上前缀(迭代器存放位置下标)
        return $this->index.'-'.$key;
    }

}

//自定义的MyIterator类
class MyIterator implements Iterator
{
    protected $data = array();  //存放数据
    protected $index=0 ;         //存放当前指针
    //构造方法接收外部传入数据
    public function __construct(Array $data)
    {   
        $this->data = $data;
    }
    //返回当前指针指向数据
    public function current()
    {   
        echo __METHOD__,PHP_EOL;
        return $this->data[$this->index];
    }
    //指针+1
    public function next()
    {   
        echo __METHOD__,PHP_EOL;
        $this->index ++;
    }
    //验证指针是否越界
    public function valid()
    {   
        echo __METHOD__,PHP_EOL;
        return $this->index ‹ count($this->data);
    }
    //重置指针
    public function rewind()
    {   
        echo __METHOD__,PHP_EOL;
        $this->index = 0;
    }
    //返回当前指针
    public function key()
    {   
        echo __METHOD__,PHP_EOL;
        return $this->index;
    }
}
//自定义迭代器实现IteratorAggregate接口
class MyIteratorAggregate implements IteratorAggregate{
    protected $arr = array();
    public function __construct($arr){
        $this->arr = $arr;
    }
    //IteratorAggregate接口要求实现的方法
    public function getIterator(){
        echo __METHOD__,PHP_EOL;
        //返回一个实现Iterator接口类的实例
        return new MyIterator($this->arr);
    }
}

$arr1=array(1,2);
$arr2=array(3,4);
$iterators = array(new MyIteratorAggregate($arr1),new MyIteratorAggregate($arr2));
//实例化一个MyOuterIterator迭代器
$container = new MyOuterIterator($iterators);
//使用MyOuterIterator迭代器
foreach($container as $key => $val){
    echo $key.'=>'.$val.PHP_EOL;
}

  运行结果:

  感觉有点复杂,可能我打开的方式不对,大家如果有可以同时支持Iterator和IteratorAggregate的实现方法,麻烦联系我(fxlxzf10@163.com),让我也能学习。谢谢。

  庆幸地是,PHP中已经有一个内置的实现OuterIterator接口,可以顺序访问不同类型迭代器的迭代器。它就是AppendIterator迭代器,继承自IteratorIterator迭代器。

  AppendIterator并没有直接实现OuterIterator,但它的父类IteratorIterator实现了OuterIterator。关于AppendIterator和IteratorIterator,我将会在SPL内置迭代器中介绍。欢迎大家到时候交流指正

  OuterIterator接口通常很少直接实现,经常是通过继承IteratorIterator。

来源:慕课手记 https://www.imooc.com/article/17906

转载请注明出处:https://www.onexin.net/spl-outeriterator-interface/

相关文章:

1、SPL迭代器接口(六)—RecursiveIterator Interface
https://www.onexin.net/spl-recursiveiterator-interface/

2、SPL迭代器接口(四)—IteratorAggregate Interface
https://www.onexin.net/spl-iteratoraggregate-interface/

3、SPL迭代器接口(三)—SeekableIterator Interface
https://www.onexin.net/spl-seekableiterator-interface/

4、SPL迭代器接口(二)—Iterator Interface
https://www.onexin.net/spl-iterator-interface/

5、SPL迭代器接口(一)—Traversable Interface
https://www.onexin.net/spl-traversable-interface/

Leave a Reply