【PHPデザインパターン】22_Strategy~戦略を切り替える


引用記事

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

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

Do You PHP はてな〜[doyouphp][phpdp]PHPによるデザインパターン入門 - Strategy~戦略を切り替える

概要

  • 「strategy」は「戦略」「方針」という意味。
  • アルゴリズムをクラスとして定義し、切り替えられるようにする。
  • それぞれの処理がクラスにまとめられて実装されるので、コードは処理内容に専念することができる。

構成要素

Strategyクラス

  • それぞれの処理に共通のAPIを定義するクラス。

ConcreteStrategyクラス

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

Contextクラス

  • Strategy型のオブジェクトを内部に保持し、具体的な処理をそのオブジェクトに委譲するクラス。

実演

処理の流れ

  • データを読み込み、ItemDataContext型オブジェクトを生成する。
  • 読み込みむためのアルゴリズムをデータ形式で分ける。
  • ReadArrayStrategyクラスは配列データを読み込む。
  • ReadStringStrategyクラスは文字列データを読み込む。

ファイル構造

MyStrategy
  ├── ItemDataContext.php
  ├── ReadArrayStrategy.php
  ├── ReadItemDataStrategy.php
  ├── ReadStringStrategy.php
  └── my_client.php

ソースコード

Strategyクラス

ReadItemDataStrategy.php

ReadItemDataStrategy.php
<?php
namespace DoYouPhp\PhpDesignPattern\Strategy\MyStrategy;

/**
 * Strategyに相当する
 */
abstract class ReadItemDataStrategy
{
    private $item_data;

    // コンストラクタ
    public function __construct($item_data)
    {
        $this->itemData = $item_data;
    }

    // データを読み込み、stdClass型オブジェクトを要素とする配列を返す
    public function getData()
    {
        return $this->readData($this->getitemData());
    }

    // 元データを返す
    public function getitemData()
    {
        return $this->itemData;
    }

    // ConcreteStrategyに実装させるメソッド
    abstract protected function readData($item_data);
}

ConcreteStrategyクラス

ReadArrayStrategy.php

ReadArrayStrategy.php
<?php
namespace DoYouPhp\PhpDesignPattern\Strategy\MyStrategy;

use DoYouPhp\PhpDesignPattern\Strategy\MyStrategy\ReadItemDataStrategy;

/**
 * ConcreteStrategyに相当する
 * 配列形式のデータを読み込む
 */
class ReadArrayStrategy extends ReadItemDataStrategy
{
    // データを読み込み、stdClass型オブジェクトを要素とする配列を返す
    protected function readData($item_data)
    {
        foreach($item_data as $item_name) {
          $obj = new \stdClass();
          $obj->item_name = $item_name;

          $return_value[] = $obj;
        }

        return $return_value;
    }
}

ReadStringStrategy.php

ReadStringStrategy.php
<?php
namespace DoYouPhp\PhpDesignPattern\Strategy\MyStrategy;

use DoYouPhp\PhpDesignPattern\Strategy\MyStrategy\ReadItemDataStrategy;

/**
 * ConcreteStrategyに相当する
 * 文字列形式のデータを読み込む
 */
class ReadStringStrategy extends ReadItemDataStrategy
{
    // データを読み込み、stdClass型オブジェクトを要素とする配列を返す
    protected function readData($item_data)
    {
        $item_array = explode(",", $item_data);

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

          $return_value[] = $obj;
        }

        return $return_value;
    }
}

Contextクラス

ItemDataContext.php

ItemDataContext.php
<?php
namespace DoYouPhp\PhpDesignPattern\Strategy\MyStrategy;

use DoYouPhp\PhpDesignPattern\Strategy\MyStrategy\ReadItemDataStrategy;

/**
 * Contextに相当する
 * Strategy型オブジェクトを保持する
 */
class ItemDataContext
{
    private $strategy;

    // コンストラクタ
    public function __construct(ReadItemDataStrategy $strategy)
    {
        $this->strategy = $strategy;
    }

    // 各ConcreteStrategyで生成されたデータを返す
    public function getItemData()
    {
        return $this->strategy->getData();
    }
}

Client

my_client.php

my_client.php
<?php
namespace DoYouPhp\PhpDesignPattern\Strategy\MyStrategy;

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

use DoYouPhp\PhpDesignPattern\Strategy\MyStrategy\ItemDataContext;
use DoYouPhp\PhpDesignPattern\Strategy\MyStrategy\ReadArrayStrategy;
use DoYouPhp\PhpDesignPattern\Strategy\MyStrategy\ReadStringStrategy;

// データ出力用メソッド
function dumpData($data)
{
    foreach ($data as $object) {
        echo $object->item_name.'<br>'."\n";
    }
}

// テスト用データ
$item_array  = array('apple', 'orange', 'peach');
$item_string = 'apple,orange,peach';

// 配列からオブジェクトを生成する
echo '--- 配列から生成したデータ ---'.'<br>'."\n";
$strategy = new ReadArrayStrategy($item_array);
$context = new ItemDataContext($strategy);
dumpData($context->getItemData());

// 文字列からオブジェクトを生成する
echo '--- 文字列から生成したデータ ---'.'<br>'."\n";
$strategy = new ReadStringStrategy($item_string);
$context = new ItemDataContext($strategy);
dumpData($context->getItemData());