1. Home
  2. PHP
  3. DesignPatterns
  4. Commandパターン - PHPデザインパターン

Commandパターン - PHPデザインパターン

  • 公開日
  • 更新日
  • カテゴリ:DesignPatterns
  • タグ:PHP,Command,Behavior,DesignPatterns
Commandパターン - PHPデザインパターン

Command パターン(コマンド・パターン)は、振る舞いに関するデザインパターン手法の1つで、ある機能に於いて処理の対象・要求・管理の関係性を築く事で、柔軟な処理の取り回しを行える処理モデルです。

Command パターンの基本的なクラス図は以下になります。

実装例

今回は、あるラーメン屋さんが注文をさばく一連の流れを Command パターンで実装します。

  • 注文を取る
  • 厨房に伝える
  • 料理を作る

大体こんなところでしょうか。

まずは Receiver クラスです。 Command クラスが処理するラーメンオブジェクトを作成します。

App/Receiver/Ramen.php
<?php

namespace App\Receiver;

class Ramen
{
    private $name;
    private $ramen;

    public function setName($name)
    {
        $this->name = sprintf('%sラーメン', $name);
    }

    public function getName()
    {
        return $this->name;
    }

    public function setRamen($ramen)
    {
        $this->ramen = $ramen;
    }

    public function getRamen()
    {
        return $this->ramen;
    }

    public function call()
    {
        echo sprintf('%s できたよー!<br>', $this->getName());
    }
}

料理の名前とデータのゲッタセッタ、そして処理後の呼びかけを行う call() メソッドを定義しています。

次に、Command クラスです。インターフェイスを定義します。

App/Command/Interfaces/CommandInterface.php
<?php

namespace App\Command\Interfaces;

interface CommandInterface
{
    public function execute();
}

具体的な命令を実装する Command 具象クラスの共通処理として execute() メソッドを定義します。

次は Command 具象クラスです。ラーメンなので「醤油」「味噌」「塩」「豚骨」のクラスを作成します。

App/Command/ShouyuCommand.php
<?php

namespace App\Command;

use App\Command\Interfaces\CommandInterface;
use App\Receiver\Ramen;

class ShouyuCommand implements CommandInterface
{
    private $category = 'しょうゆ';

    private $ramen;

    public function __construct(Ramen $ramen)
    {
        $this->ramen = $ramen;
        $this->ramen->setName($this->category);
    }

    public function execute()
    {
        /*
         * 醤油ラーメンを作る処理
         */

        $this->ramen->call();
    }
}
App/Command/MisoCommand.php
<?php

namespace App\Command;

use App\Command\Interfaces\CommandInterface;
use App\Receiver\Ramen;

class MisoCommand implements CommandInterface
{
    private $category = 'みそ';

    private $ramen;

    public function __construct(Ramen $ramen)
    {
        $this->ramen = $ramen;
        $this->ramen->setName($this->category);
    }

    public function execute()
    {
        /*
         * 味噌ラーメンを作る処理
         */

        $this->ramen->call();
    }
}
App/Command/ShioCommand.php
<?php

namespace App\Command;

use App\Command\Interfaces\CommandInterface;
use App\Receiver\Ramen;

class ShioCommand implements CommandInterface
{
    private $category = 'しお';

    private $ramen;

    public function __construct(Ramen $ramen)
    {
        $this->ramen = $ramen;
        $this->ramen->setName($this->category);
    }

    public function execute()
    {
        /*
         * 塩ラーメンを作る処理
         */

        $this->ramen->call();
    }
}
App/Command/TonkotsuCommand.php
<?php

namespace App\Command;

use App\Command\Interfaces\CommandInterface;
use App\Receiver\Ramen;

class TonkotsuCommand implements CommandInterface
{
    private $category = 'とんこつ';

    private $ramen;

    public function __construct(Ramen $ramen)
    {
        $this->ramen = $ramen;
        $this->ramen->setName($this->category);
    }

    public function execute()
    {
        /*
         * とんこつラーメンを作る処理
         */

        $this->ramen->call();
    }
}

コンストラクタで Receiver オブジェクトを受け取り、命令に関する処理であるインターフェイスを実装しています。今回は特に処理は行わず、処理完了を示す call() メソッドだけを呼んでいます。

最後に、Invoker クラスです。このクラスは命令を保持し、処理を実行します。

App/Invoker/Queue.php
<?php

namespace App\Invoker;

use App\Command\Interfaces\CommandInterface AS Command;

class Queue
{
    private $commands;

    public function __construct()
    {
        $this->commands = [];
    }
    public function addCommand(Command $command)
    {
        $this->commands[] = $command;
    }

    public function run()
    {
        if (!empty($this->commands)) {
           foreach ($this->commands as $command) {
               $command->execute();
           }
           $this->commands = [];
        }
    }
}

addCommand() メソッドで命令を追加し、run() メソッドでそれらを順番に実行していきます。

実装が完了したので、クライアントから利用してみます。

index.php
<?php

use App\Invoker\Queue;
use App\Receiver\Ramen;
use App\Command\ShouyuCommand;
use App\Command\MisoCommand;
use App\Command\ShioCommand;
use App\Command\TonkotsuCommand;

$queue = new Queue();

// 4人分のオーダーです。
$queue->addCommand(new ShouyuCommand(new Ramen()));
$queue->addCommand(new MisoCommand(new Ramen()));
$queue->addCommand(new ShioCommand(new Ramen()));
$queue->addCommand(new TonkotsuCommand(new Ramen()));

$queue->run();

echo '<hr>';

// 次は、5人分のオーダーです。
$queue->addCommand(new TonkotsuCommand(new Ramen()));
$queue->addCommand(new MisoCommand(new Ramen()));
$queue->addCommand(new TonkotsuCommand(new Ramen()));
$queue->addCommand(new ShouyuCommand(new Ramen()));
$queue->addCommand(new ShioCommand(new Ramen()));

$queue->run();

Receiver を吸収した Command を Invoker の中へ登録し、最後に Invoker が run() メソッドで命令を全て実行します。

結果は以下になります。

Command パターンを用いて要求の送受信と処理実行のロジックを分離出来た事を確認できました。

まとめ

「Command = 命令」を意味する通り、処理の要求自体をオブジェクト化し処理対象のオブジェクトとの間に要求を管理・実行するオブジェクトを挟む事で、処理の追加や拡張を容易にしたり、処理の順番や結果自体を管理できている事で、キューやワーカーのような使い方や、処理の巻き戻しといった事も行えるようになります。

設計次第で色々な使い方が出来るので、是非試してみてください。

Author

rito

  • Backend Engineer
  • Tokyo, Japan
  • PHP 5 技術者認定上級試験 認定者
  • 統計検定 3 級