PHP7でデザインパターン入門22/23 Commandパターン

Command パターンとは

動作を表現するオブジェクトを示す。Command オブジェクトは、動作とそれに伴うパラメータをカプセル化したものである。

Command パターン - Wikipedia

クラスが仕事を行う時、自分のクラスや他のクラスのメソッドを呼び出す。メソッドを呼び出した結果はオブジェクトの状態に反映されるが、履歴はどこにも残らない。 こんなとき、仕事の「命令」を表現するクラスがあれば便利。行いたい仕事を「メソッドを呼び出す」という動的な処理として表現するのではなく、命令を表すクラスのインスタンスという1個の「もの」として表現することができるからである。履歴を管理したいときは、そのインスタンスの集まりを管理すればいいことになる。命令をの集まりを保存しておけば、同じ命令の再実行もできる。

デザインパターンではこのような「命令」にCommandパターンという名前をつけている。CommandはEventと呼ばれる場合もある。

example

サンプルコードとして、wikiJavaサンプルコードを引用する。

Consider a “simple” switch. In this example we configure the Switch with two commands: to turn the light on and to turn the light off. A benefit of this particular implementation of the command pattern is that the switch can be used with any device, not just a light. The Switch in the following C# implementation turns a light on and off, but the Switch’s constructor is able to accept any subclasses of Command for its two parameters. For example, you could configure the Switch to start an engine.

Commandインターフェース

<?php
interface Command
{
    public function execute();
    public function __toString(): string;
}

MySwitchクラス

<?php
class MySwitch
{
    private $history = [];

    public function storeAndExecute(Command $cmd)
    {
        $this->history[] = $cmd;
        $cmd->execute();
    }

    public function displayHistory()
    {
        foreach($this->history as $k => $v) {
            echo "{$k}: {$v}" . PHP_EOL;
        }
    }
}

Lightクラス

<?php
class Light
{
    public function turnOn()
    {
        echo "The light is on" . PHP_EOL;
    }

    public function turnOff()
    {
        echo "The light is off" . PHP_EOL;
    }
}

FlipUpCommandクラス

<?php
class FlipUpCommand implements Command
{
    private $theLight;

    public function FlipUpCommand(Light $light)
    {
        $this->theLight = $light;
    }

    public function execute()
    {
        $this->theLight->turnOn();
    }

    public function __toString() : string
    {
        return get_class($this);
    }
}

FlipDownCommandクラス

<?php
class FlipDownCommand implements Command
{
    private $theLight;

    public function FlipDownCommand(Light $light)
    {
        $this->theLight = $light;
    }

    public function execute()
    {
        $this->theLight->turnOff();
    }

    public function __toString(): string
    {
        return get_class($this);
    }
}

Main

<?php
$lamp = new Light();
$switchUp = new FlipUpCommand($lamp);
$switchDown = new FlipDownCommand($lamp);
$mySwitch = new MySwitch();

for($i = 0; $i < 10; $i++) {
    if (boolval(rand(0, 1))) {
        $mySwitch->storeAndExecute($switchUp);
    } else {
        $mySwitch->storeAndExecute($switchDown);
    }
    usleep(50000); // wait 0.5 sec
}
echo PHP_EOL . "=== command history ===" . PHP_EOL;
$mySwitch->displayHistory();

実行結果

$ php Main.php
The light is on
The light is off
The light is off
The light is off
The light is off
The light is off
The light is on
The light is on
The light is off
The light is off

=== command history ===
0: FlipUpCommand
1: FlipDownCommand
2: FlipDownCommand
3: FlipDownCommand
4: FlipDownCommand
5: FlipDownCommand
6: FlipUpCommand
7: FlipUpCommand
8: FlipDownCommand
9: FlipDownCommand

ソースコード

github.com

役割

Command役

命令のインターフェースを定義する役。 サンプルコードにおけるCommandインターフェース。

ConcreteCommand役

Command役のインターフェースを実装している役。 サンプルコードにおけるFlipUpCommandクラス、FlipDownCommandクラス。

Receiver役

Command役が命令を実行するときに対象となる役。命令の受け取り手とも呼べる。 サンプルコードにおけるLightクラス。

Client役

ConcreteCommand役を生成し、その際にReceiver役を割り当てる役。 サンプルコードにおけるMain.php

Invoker役

命令の実行を開始する役。Command役で定義されているインターフェースを呼び出す役となる。 サンプルコードにおけるMySwitchクラス。

まとめ

「命令」をオブジェクトとして表現することで、履歴をとったり再実行を行ったりすることが可能となる。

関連パターン

増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門