4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

デザインパターン攻略:Command編

Last updated at Posted at 2025-11-17

Commandパターンとは?

デザインパターンの一種で、命令や動作をオブジェクトで表現する設計技法のことです。
イメージとしては1クラスの中に1命令の実行に必要な手続きとデータが収まっている感じです。

wikiには以下のように記載されています。

Commandパターンでは何かリクエストを実行する際、単純に処理を実行するのではなく、次のステップを踏む。
1.処理をメソッドとして内包するCommandクラスの定義
2.Commandオブジェクトの生成
3.Command.execute()メソッドのコールによるリクエスト実行
すなわちリクエストを「手順書」の定義・生成とその「実行」に段階分けするパターンをとる。

クラス図

メリット

  • 実行単位ごとにクラス分けしておくことによって各Commandが疎結合に
  • Invokerによる実行順序の一元管理で実行履歴保存やundoが容易になる
  • ClientとCommandの責務の分離が明確になる

C#の実装例

さっそく実装してみます!
今回は二つの数の和と差を求めるプログラムを作ります。

ICommand

各Commandの元となるインターフェース

public interface ICalcCommand
{
    void Execute();
}

Receiver

Commandから呼び出される内部の処理を実装します。

public class Receiver
{
    public void AddAction(double first, double second)
    {
        Console.WriteLine($"和: {first + second}");
    }

    public void SubtractAction(double first, double second)
    {
        Console.WriteLine($"差: {first - second}");
    }
}

ConcreteCommand

インターフェースの実装クラスです。
注目ポイントは、各Commandの細かい処理の実装はReceiverに委譲しているということです。

// 足し算
public class AddCalcCommand : ICalcCommand
{
    private readonly Receiver _receiver;
    private readonly double _first;
    private readonly double _second;

    public AddCalcCommand(Receiver receiver, double first, double second)
    {
        this._receiver = receiver;
        this._first = first;
        this._second = second;
    }

    public void Execute()
    {
        _receiver.AddAction(_first, _second); // Recieverの処理を呼び出し
    }
}

// 引き算
public class SubtractCalcCommand : ICalcCommand
{
    private readonly Receiver _receiver;
    private readonly double _first;
    private readonly double _second;

    public SubtractCalcCommand(Receiver receiver, double first, double second)
    {
        this._receiver = receiver;
        this._first = first;
        this._second = second;
    }
    public void Execute()
    {
        this._receiver.SubtractAction(_first, _second); // Recieverの処理を呼び出し
    }
}

Invoker

Commandの格納と呼び出しを管理するInvokerです。
順序の管理と一斉呼び出しを担います。

public class Invoker
{
    private readonly Queue<ICalcCommand> _calcCommands = new Queue<ICalcCommand>();

    public void AddCommand(ICalcCommand command)
    {
        this._calcCommands.Enqueue(command);
    }

    public void ExecuteAll()
    {
        while (_calcCommands.Count > 0)
        {
            var current = _calcCommands.Dequeue();
            current.Execute();
        }
    }
}

Client

後はClientから作り上げたパターンを呼び出すだけです。

public class Program
{
    public static void Main()
    {
        var firstNum = 50;
        var secondNum = 30;

        var receiver = new Receiver();
        var invoker = new Invoker();

        invoker.AddCommand(new AddCalcCommand(receiver, firstNum, secondNum));
        invoker.AddCommand(new SubtractCalcCommand(receiver, firstNum, secondNum));

        Console.WriteLine($"{firstNum}{secondNum}の和と差は?");
        invoker.ExecuteAll();
    }
}

実行結果

50と30の和と差は?
和: 80
差: 20

疑問に思ったこと

「Receiverって本当に必要なの?🤔」

Reciverに分けなくてもConcreteCommandに内部処理そのまま実装してしまえばよくないか?とCommandパターン初心者の私は思ったわけです。

wikiを見るとReceiverは必ずしもCommandパターンの要件ではないようです...
が、以下の点からReceiverを設けて処理を委譲するメリットは大きいとのことです。

  1. 処理の実体がConcreteCommandに混ざらず、設計が単純になる
  2. 似た処理を持つ複数のCommandのが増えなくなる
  3. テストやモックがしやすくなる

ぜひ今後の実装の一案に加えてみてはいかがでしょうか🙆‍♂️

参考

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?