【PHPデザインパターン】23_Visitor～要素と要素に対する操作を分離する

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

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

> [Do You PHP はてな〜[doyouphp][phpdp]PHPによるデザインパターン入門 - Visitor～要素と要素に対する操作を分離する](http://d.hatena.ne.jp/shimooka/20141219/1418965547)

# 概要
- 「visitor」は「訪問者」という意味。
- データ構造上の要素とそれに対する操作を分離する。
- データ構造から分離された「操作」がデータ構造を渡り歩き、順に処理を行っていく。

## 構成要素
### Visitorクラス
- 操作のクラス階層で最上位に位置するクラス。

### ConcreteVisitorクラス
- Visitorクラスのサブクラス。

### Elementクラス
- データ構造のクラス階層で最上位に位置するクラス。
- Visitor型のオブジェクトを受け入れるためのメソッドを宣言する。

### ConcreteElementクラス
- Elementクラスのサブクラス。

### ObjectStructureクラス
- Elementクラスの集合を表すクラス。

# 実演
## 処理の流れ
- ``ItemObjArray型オブジェクト``をデータ構造とする。プロパティとして、``stdClass型オブジェクト``を要素とする配列を保持している。
- ``ItemObjArrayクラス``でVistor型オブジェクトを受け入れるメソッドを用意する。
- ``DumpVisitorクラス``から各要素のプロパティを出力する。
- ``CountVisitorクラス``から要素数を出力する。
- 今回はConcreteElementクラスを作成していない。

## ファイル構造
```sh
MyVisitor
  ├── CountVisitor.php
  ├── DumpVisitor.php
  ├── ItemObjArray.php
  ├── Visitor.php
  └── my_client.php
```

## ソースコード
### Visitorクラス
#### Visitor.php
```Visitor.php
<?php
namespace DoYouPhp\PhpDesignPattern\Visitor\MyVisitor;

use DoYouPhp\PhpDesignPattern\Visitor\MyVisitor\ItemObjArray;

/**
 * Visitorクラスに相当する
 */
interface Visitor
{
    public function visit(ItemObjArray $item_obj_array);
}
```

### ConcreteVisitorクラス
#### DumpVisitor.php
```DumpVisitor.php
<?php
namespace DoYouPhp\PhpDesignPattern\Visitor\MyVisitor;

use DoYouPhp\PhpDesignPattern\Visitor\MyVisitor\Visitor;
use DoYouPhp\PhpDesignPattern\Visitor\MyVisitor\ItemObjArray;

/**
 * ConcreteVisitorクラスに相当する
 */
class DumpVisitor implements Visitor
{
    // ItemObjArray型オブジェクトで保持しているstdClass型オブジェクトのプロパティを返す
    public function visit(ItemObjArray $item_obj_array)
    {
      foreach ($item_obj_array->getObjArray() as $obj) {
          echo $obj->item_name.'<br>'."\n";
      }
    }
}
```

#### CountVisitor.php
```CountVisitor.php
<?php
namespace DoYouPhp\PhpDesignPattern\Visitor\MyVisitor;

use DoYouPhp\PhpDesignPattern\Visitor\MyVisitor\Visitor;
use DoYouPhp\PhpDesignPattern\Visitor\MyVisitor\ItemObjArray;

/**
 * ConcreteVisitorクラスに相当する
 */
class CountVisitor implements Visitor
{
    // ItemObjArray型オブジェクトで保持しているstdClass型オブジェクトの数を返す
    public function visit(ItemObjArray $item_obj_array)
    {
        echo count($item_obj_array->getObjArray());
    }
}
```

### Elementクラス
兼ObjectStructureクラス。

#### ItemObjArray.php
```ItemObjArray.php
<?php
namespace DoYouPhp\PhpDesignPattern\Visitor\MyVisitor;

use DoYouPhp\PhpDesignPattern\Visitor\MyVisitor\Visitor;

/**
 * Elementクラス兼ObjectStructureクラスに相当する
 * Visitor型のオブジェクトを受け入れるためのメソッドを宣言する
 */
class ItemObjArray
{
    private $item_obj_array;

    // 配列の要素をstdClass型オブジェクト化して、配列型プロパティに加える
    public function __construct($item_array)
    { 
        $item_obj_array = array();

        foreach($item_array as $item_name) {
            $obj = new \stdClass();
            $obj->item_name = $item_name;

            $this->item_obj_array[] = $obj;
        }
    }

    // 配列型プロパティを返す
    public function getObjArray()
    {
        return $this->item_obj_array;
    }

    // Visitor型のオブジェクトを受け入れるためのメソッド
    public function accept(Visitor $visitor)
    {
        $visitor->visit($this);
    }
}
```

### Client
#### my_client.php
```my_client.php
<?php
namespace DoYouPhp\PhpDesignPattern\Visitor\MyVisitor;

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

use DoYouPhp\PhpDesignPattern\Visitor\MyVisitor\DumpVisitor;
use DoYouPhp\PhpDesignPattern\Visitor\MyVisitor\CountVisitor;

    // ItemObjArray型オブジェクトを生成する
    $item_array = array('apple', 'orange', 'peach');
    $item_obj_array = new ItemObjArray($item_array);

    // ItemObjArray型オブジェクトで保持しているstdClass型オブジェクトのプロパティを返す
    $item_obj_array->accept(new DumpVisitor());

    // 同じインスタンスに対して別のVisitorを使用する
    // ItemObjArray型オブジェクトで保持しているstdClass型オブジェクトの数を返す
    $visitor = new CountVisitor();
    $item_obj_array->accept($visitor);
```