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


引用記事

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

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

Do You PHP はてな〜[doyouphp][phpdp]PHPによるデザインパターン入門 - Visitor~要素と要素に対する操作を分離する

概要

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

構成要素

Visitorクラス

  • 操作のクラス階層で最上位に位置するクラス。

ConcreteVisitorクラス

  • Visitorクラスのサブクラス。

Elementクラス

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

ConcreteElementクラス

  • Elementクラスのサブクラス。

ObjectStructureクラス

  • Elementクラスの集合を表すクラス。

実演

処理の流れ

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

ファイル構造

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);