【PHPデザインパターン】06_Iterator～順々にアクセスする

# 引用記事
この記事を書くきっかけになったブログです。

記事内の解説やソースコードは、こちらのブログと著者の公開リポジトリを参考にしています。

> [Do You PHP はてな〜[doyouphp][phpdp]PHPによるデザインパターン入門 - Iterator～順々にアクセスする](http://d.hatena.ne.jp/shimooka/20141215/1418620355)

# 概要
- オブジェクトに対する反復操作をおこなうための統一APIを提供する。
- 具体的には、利用者にリスト（集約オブジェクト）の内部構造を隠したまま、それぞれの要素にアクセスさせるためのAPIを提供する。
- 実装することにより、異なる内部構造を持つリストの要素に同じAPIでアクセスできる。
- 「iterate」は「繰り返す」「反復する」という意味。

## 構成要素
### Iteratorクラス
- 要素にアクセスするためのAPIを提供するクラス。

### ConcreteIteratorクラス
- Iteratorクラスのサブクラス。
- リストの内部構造を把握し、それに依存する走査処理を実装する。

### Aggregateクラス
- Iteratorオブジェクトを返すAPIを提供するクラス。
- クライアントの窓口に当たる。

### ConcreteAggregateクラス
- Aggregateクラスのサブクラス。
- リスト固有のIteratorオブジェクトを返す。

## SPLについて
``SPL（Standard PHP Library）``は標準的な問題を解決するためのインターフェイスやクラスを集めたものです。
PHP5.0.0以降はデフォルトで使用できるそうです。

> [php.net〜Standard PHP Library (SPL)](http://php.net/manual/ja/book.spl.php)

SPLにはイテレータが用意されており、オブジェクトを反復処理することができます。

> [php.net〜イテレータ](http://php.net/manual/ja/spl.iterators.php)

# 実演
著者のソースコードとほぼ同じです。

## 処理の流れ
- 国名、大陸、言語の登録と出力を行う。
- SPLのイテレータを利用する。
- ``Countriesクラス``はConcreteAggregateクラスに相当する。
- Countriesクラスが実装している``IteratorAggregateインターフェース``はAggregateクラスに相当する。
- Countriesクラスで生成している``ArrayObjectクラス``は、オブジェクトを配列として扱うためのクラス。そこで配列用のiteratorクラスである``ArrayIteratorクラス``が利用される。
- ``AsiaIteratorクラス``はConcreteIteratorクラスに相当する。大陸がAsiaの国だけを出力できるようにフィルタリングを行う。

## ファイル構造
```
 MyIterator
   ├── AsiaIterator.php
   ├── Countries.php
   ├── Country.php
   └── my_client.php
```

## ソースコード 
### my_client.php
```my_client.php
<?php
namespace DoYouPhp\PhpDesignPattern\Iterator\MyIterator;

require dirname(dirname(__DIR__)).'/vendor/autoload.php';

use DoYouPhp\PhpDesignPattern\Iterator\MyIterator\Country;
use DoYouPhp\PhpDesignPattern\Iterator\MyIterator\Countries;
use DoYouPhp\PhpDesignPattern\Iterator\MyIterator\Asia;

// Countriesインスタンスを作成する
// その中に配列の要素としてCountryインスタンスをセットする
$countries = new Countries();
$countries->add(new Country('Japan', 'Asia', 'Japanese'));
$countries->add(new Country('America', 'North America', 'English'));
$countries->add(new Country('China', 'Asia', 'Chinese'));

// イテレータを取得する
$iterator = $countries->getIterator();
// フィルタリングされたイテレータを取得する
$asia_iterator = new AsiaIterator($iterator);

// foreach構文を利用して配列の要素を取り出す
function dumpWithForeach(\Iterator $iterator)
{
    foreach ($iterator as $country) {
        echo '国名:'.$country->getName()."\t".'大陸:'.$country->getContinent()."\t".'言語:'.$country->getLanguage().'<br>'."\n";
    }
}

// Iteratorクラスのメソッドを利用して配列の要素を取り出す
// validメソッドで現在の要素が有効かチェックする
// 有効であればtrueが返り、処理が実行される
function dumpWithIterator(\Iterator $iterator)
{
    while ($iterator->valid()) {
        // currentメソッドで現在の要素の値を取得する
        $country = $iterator->current();
        echo '国名:'.$country->getName()."\t".'大陸:'.$country->getContinent()."\t".'言語:'.$country->getLanguage().'<br>'."\n";

        // nextメソッドでイテレータを前方に移動する
        $iterator->next();
    }
}

// イテレータのメソッドを利用する
echo 'これはIteratorクラスのメソッドです'.'<br>'."\n";
dumpWithIterator($iterator);

// foreach文を利用する
echo 'これはforeach文を利用しています'.'<br>'."\n";
dumpWithForeach($iterator);

// 異なるイテレータで要素を取得する
// FilterIteratorクラスの場合、配列の取り出しはforeach構文のみ利用できる
echo 'これはAsiaIteratorクラスでフィルタリングされforeach文を利用しています'.'<br>'."\n";
dumpWithForeach($asia_iterator);
```

### Country.php
```Country.php
<?php
namespace DoYouPhp\PhpDesignPattern\Iterator\MyIterator;

// 追加する要素の各値をセットする
class Country
{
    private $name;
    private $continent;
    private $language;

    public function __construct($name, $continent, $language)
    {
        $this->name = $name;
        $this->continent = $continent;
        $this->language = $language;
    }

    // 各値を取得する
    public function getName()
    {
        return $this->name;
    }

    public function getContinent()
    {
        return $this->continent;
    }

    public function getLanguage()
    {
        return $this->language;
    }
}
```

### Countries.php
```Countries.php
<?php
namespace DoYouPhp\PhpDesignPattern\Iterator\MyIterator;

use DoYouPhp\PhpDesignPattern\Iterator\MyIterator\Country;

// IteratorAggregateクラスは外部イテレータを作成するためのインターフェイス
class Countries implements \IteratorAggregate
{
    // Countryインスタンスが配列の要素としてセットされる
    // ArrayObjectクラスのインスタンス
    private $countries;

    // ArrayObjectクラスはオブジェクトを配列として動作させる
    public function __construct()
    {
        $this->countries = new \ArrayObject();
    }

    // 配列に新しいCountryインスタンスを追加する
    public function add(Country $country)
    {
        $this->countries[] = $country;
    }

    // インターフェイスのメソッド
    // イテレータを取得して返す
    // 戻り値はIteratorクラスのオブジェクト
    public function getIterator()
    {
        return $this->countries->getIterator();
    }
}
```

### AsiaIterator.php
```AsiaIterator.php
<?php
namespace DoYouPhp\PhpDesignPattern\Iterator\MyIterator;

use DoYouPhp\PhpDesignPattern\Iterator\MyIterator\Country;

// FilterIteratorクラスはフィルタリングを行う抽象クラス
class AsiaIterator extends \FilterIterator
{
    // コンストラクタ
    public function __construct($iterator)
    {
        parent::__construct($iterator);
    }

    // FilterIteratorクラスの抽象メソッド
    // 配列の要素数だけ繰り返し実行される
    public function accept()
    {
        // getInnerIteratorメソッドで内部イテレータを取得する
        // currentメソッドで現在の要素の値を取得する
        // 両方ともFilterIteratorクラスのメソッド
        $country = $this->getInnerIterator()->current();

        // continentがAsiaの要素のみ抽出する
        return ($country->getContinent() === 'Asia');
    }
}
```