【PHPデザインパターン】11_Command～要求をクラスで表す

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

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

> [Do You PHP はてな〜[doyouphp][phpdp]PHPによるデザインパターン入門 - Command～要求をクラスで表す](http://d.hatena.ne.jp/shimooka/20141216/1418705218)

# 概要
- 「要求」そのものをクラスとして表し、「要求を送る側」と「要求を受け取る側」を分離する。
- 異なる種類の要求に対する処理を、同じAPIを持つクラスとして実装する。
- 新しい要求に対する処理クラスを実装するだけで、既存のクラスを修正することなく対応可能である。

## 構成要素
### Commandクラス
- 命令を実行するためのAPIを定義するクラス。
- 実行したコマンド結果を保持しておくことで、Undo機能（直前の操作の取り消し）やRedo機能（取り消した操作のやり直し）を実現することができる。

### ConcreteCommandクラス
- Commandクラスのサブクラス。

### Invokerクラス
- 命令実行の要求を出すクラス。

### Receiverクラス
- 命令をどの様に実行するかを知っている唯一のクラス。
- 任意のクラスがReceiverクラスになれる。

# 実演
## 処理の流れ
- ``Queueクラス``で命令実行の指示を出し、``Commandクラス``のオブジェクトをセットする。
- ``UpperCommandクラス``では、文字列を大文字に変更する。
- ``KanaCommandクラス``では、文字列を全角に変更する。
- 上記2つの実行手順は``Wordクラス``で定義されている。

## ファイル構造
```sh
MyCommand
  ├── Command.php
  ├── KanaCommand.php
  ├── Queue.php
  ├── UpperCommand.php
  ├── Word.php
  └── my_client.php
```

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

/**
 * Commandクラスに相当する
 * 実装はサブクラスで行う
 */
interface Command
{
    public function execute();
}
```

### ConcreteCommandクラス
#### UpperCommand.php
```UpperCommand.php
<?php
namespace DoYouPhp\PhpDesignPattern\Command\MyCommand;

use DoYouPhp\PhpDesignPattern\Command\MyCommand\Command;
use DoYouPhp\PhpDesignPattern\Command\MyCommand\Word;

/**
 * ConcreteCommandクラスに相当する
 * Commandクラスのメソッドを実装する
 * 文字列を大文字に変える命令を実行する
 */
class UpperCommand implements Command
{
    private $word;

    public function __construct(Word $word)
    {
        $this->word = $word;
    }
    public function execute()
    {
        $this->word->convertUpper();
    }
}
```

#### KanaCommand.php
```KanaCommand.php
<?php
namespace DoYouPhp\PhpDesignPattern\Command\MyCommand;

use DoYouPhp\PhpDesignPattern\Command\MyCommand\Command;
use DoYouPhp\PhpDesignPattern\Command\MyCommand\Word;

/**
 * ConcreteCommandクラスに相当する
 * Commandクラスのメソッドを実装する
 * 文字列を全角に変える命令を実行する
 */
class KanaCommand implements Command
{
    private $word;

    public function __construct(Word $word)
    {
        $this->word = $word;
    }
    public function execute()
    {
        $this->word->convertKana();
    }
}
```

### Invokerクラス
#### Queue.php
```Queue.php
<?php
namespace DoYouPhp\PhpDesignPattern\Command\MyCommand;

use DoYouPhp\PhpDesignPattern\Command\MyCommand\Command;

/**
 * Invokerクラスに相当する
 */
class Queue
{
    // Commandオブジェクトを配列としてセットする
    private $commands;
    // Commandオブジェクトの配列を回すためのカウンタ
    private $current_index;

    public function __construct()
    {
        $this->commands = array();
        $this->current_index = 0;
    }

    // Commandオブジェクトをセットする
    public function addCommand(Command $command)
    {
        $this->commands[] = $command;
    }

    // セットされたCommandオブジェクトを実行する
    public function run()
    {
        // 配列の全ての要素にアクセスする
        while (!is_null($command = $this->next())) {
            $command->execute();
        }
    }

    // 配列を回してオブジェクトをセットする
    private function next()
    {
        // commandsの数が0か、カウンタがcommandsの数を超えたら処理をストップする
        if (count($this->commands) === 0 || count($this->commands) <= $this->current_index) {
            return;
        } else {
            // 初回の$this->current_index++の値は0
            // 1ずつ加算される
            return $this->commands[$this->current_index++];
        }
    }
}
```

### Receiverクラス
#### Word.php
```Word.php
<?php
namespace DoYouPhp\PhpDesignPattern\Command\MyCommand;

/**
 * Receiverクラスに相当する
 */
class Word
{
    private $word_data;

    public function __construct($word_data)
    {
        $this->word_data = $word_data;
    }

    public function getWord()
    {
        return $this->word_data;
    }

    // 文字列を大文字に変えて出力する
    public function convertUpper()
    {
        echo strtoupper($this->word_data).'<br>'."\n";
    }

    // 文字列を全角に変えて出力する
    public function convertKana()
    {
        echo mb_convert_kana($this->word_data, "R").'<br>'."\n";
    }
}
```

### Clientクラス
#### my_client.php
```my_client.php
<?php
namespace DoYouPhp\PhpDesignPattern\Command\MyCommand;

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

use DoYouPhp\PhpDesignPattern\Command\MyCommand\Queue;
use DoYouPhp\PhpDesignPattern\Command\MyCommand\UpperCommand;
use DoYouPhp\PhpDesignPattern\Command\MyCommand\KanaCommand;
use DoYouPhp\PhpDesignPattern\Command\MyCommand\Word;

// 命令を出すクラスのオブジェクト
$queue = new Queue();
// 命令をどのように実行するか知っているクラスのオブジェクト
$word = new Word('test');
// どの命令を実行するかセットする
$queue->addCommand(new UpperCommand($word));
$queue->addCommand(new KanaCommand($word));

// 命令を実行する
$queue->run();
```