LoginSignup
2
3

More than 5 years have passed since last update.

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

Posted at

引用記事

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

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

Do You PHP はてな〜[doyouphp][phpdp]PHPによるデザインパターン入門 - Iterator~順々にアクセスする

概要

  • オブジェクトに対する反復操作をおこなうための統一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)

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

php.net〜イテレータ

実演

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

処理の流れ

  • 国名、大陸、言語の登録と出力を行う。
  • 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');
    }
}
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3