😊

デザインパターンを学ぶ #12 Command(コマンド)

に公開

1. はじめに

今回は Command(コマンド)パターン
目的は、処理(命令)をオブジェクトとして抽象化し、呼び出し元と実行先を疎結合にすることです。

処理を直接呼ばずに「命令として渡す」ことで、実行の記録・再実行・キュー処理などを柔軟に行えます。

2. Commandとは?

  • 命令をオブジェクト化(Command)
  • 実行対象(Receiver)への操作をコマンド経由で呼ぶ
  • 呼び出し元(Invoker)は中身を知らずに execute() を実行するだけ

3. 実装イメージ(PHP)

例:ライトのON/OFFを、命令として扱う

<?php

interface Command {
    public function execute(): void;
}

// 処理対象(実行者)
class Light {
    public function on(): void {
        echo "ライトON\n";
    }
    public function off(): void {
        echo "ライトOFF\n";
    }
}

// 命令(処理をオブジェクト化)
class LightOnCommand implements Command {
    public function __construct(private Light $light) {}
    public function execute(): void {
        $this->light->on();
    }
}

class LightOffCommand implements Command {
    public function __construct(private Light $light) {}
    public function execute(): void {
        $this->light->off();
    }
}

// 命令を実行する側
class RemoteControl {
    /** @var Command[] */
    private array $commands = [];

    public function add(Command $command): void {
        $this->commands[] = $command;
    }

    public function run(): void {
        foreach ($this->commands as $command) {
            $command->execute();
        }
    }
}

// --- 利用 ---
$light = new Light();
$remote = new RemoteControl();

$remote->add(new LightOnCommand($light));
$remote->add(new LightOffCommand($light));

$remote->run();

出力例:

ライトON  
ライトOFF

4. 利用シーン

  • 非同期処理やキュー投入(後から実行)
  • Undo/Redo の実装
  • マクロ(複数命令の一括実行)
  • ログ・トランザクションの記録と再実行

5. メリット

  • 呼び出し元と実行先を疎結合にできる
  • 処理の履歴管理や再実行が可能になる
  • 命令を組み合わせたり、蓄積したりできる

6. 注意点

  • 命令クラスが増えやすい
  • 単純な処理に使うと冗長になる
  • Command単体ではUndo処理などは自前実装が必要

7. 他パターンとの比較

パターン 違い
Strategy 処理の切り替えに注目(命令の保持はしない)
Memento 状態の保存と復元が目的(Commandは命令そのもの)
Observer 通知が目的(Commandは処理の移譲と記録)

8. テスト観点

  • 複数コマンドを登録 → 順に実行されるか
  • execute() による副作用が正しく起きるか
  • 同じReceiverでも異なるCommandが独立して動くか

9. まとめ

Commandパターンは、処理をオブジェクト化して切り離す構造パターン。
キュー処理・再実行・複数実行など、**「命令を貯めて実行したい場面」**で役立ちます。

Discussion