Skip to content

コマンドパターン

公開日:November 17, 2024更新日:November 28, 2024
TypeScriptDesign pattern📄

コマンドパターン

コマンドパターン(Command Pattern) は、操作をオブジェクトとしてカプセル化し、それを呼び出し元から分離することで、操作の呼び出し、取り消し、キューイングを柔軟に管理できるようにするデザインパターンです。このパターンでは、リクエストをオブジェクトとして表現し、操作を実行するオブジェクト(コマンド)と操作の受け手(レシーバー)を疎結合に保ちます。

コマンドパターンは、メニューの操作を抽象化したい場合複数の操作をまとめて管理する必要がある場合に役立ちます。たとえば、アンドゥ/リドゥの操作や、複数の操作をバッチで処理する場合に使用されます。

TypeScriptでのコマンドパターンの実装

以下に、TypeScriptでコマンドパターンを実装する例を紹介します。この例では、リモコンを使ってライトを操作するシンプルなシナリオを取り上げます。

ライトの操作の例

typescript
// コマンドインターフェース
interface Command {
  execute(): void;
  undo(): void;
}

// レシーバークラス(実際に操作を実行する)
class Light {
  turnOn(): void {
    console.log('The light is ON');
  }

  turnOff(): void {
    console.log('The light is OFF');
  }
}

// ライトを点けるコマンド
class LightOnCommand implements Command {
  private light: Light;

  constructor(light: Light) {
    this.light = light;
  }

  execute(): void {
    this.light.turnOn();
  }

  undo(): void {
    this.light.turnOff();
  }
}

// ライトを消すコマンド
class LightOffCommand implements Command {
  private light: Light;

  constructor(light: Light) {
    this.light = light;
  }

  execute(): void {
    this.light.turnOff();
  }

  undo(): void {
    this.light.turnOn();
  }
}

// インボーカークラス(コマンドを呼び出す)
class RemoteControl {
  private command: Command | null = null;

  setCommand(command: Command): void {
    this.command = command;
  }

  pressButton(): void {
    if (this.command) {
      this.command.execute();
    }
  }

  pressUndo(): void {
    if (this.command) {
      this.command.undo();
    }
  }
}

// 実際の使用例
const light = new Light();
const lightOnCommand = new LightOnCommand(light);
const lightOffCommand = new LightOffCommand(light);

const remote = new RemoteControl();

// ライトを点ける
remote.setCommand(lightOnCommand);
remote.pressButton(); // "The light is ON"
remote.pressUndo();   // "The light is OFF"

// ライトを消す
remote.setCommand(lightOffCommand);
remote.pressButton(); // "The light is OFF"
remote.pressUndo();   // "The light is ON"

解説

  1. Commandインターフェース

    • Commandインターフェースは、executeメソッドとundoメソッドを定義しています。これにより、各コマンドが実行と取り消しの操作を持つことが保証されます。
  2. Lightクラス(レシーバー)

    • Lightクラスは、実際の操作を実行する役割を持つレシーバーです。このクラスには、ライトを点けたり消したりするためのメソッドがあります。
  3. LightOnCommandとLightOffCommandクラス(具体的なコマンド)

    • LightOnCommandLightOffCommandは、Commandインターフェースを実装した具体的なコマンドクラスです。それぞれ、ライトを点ける操作と消す操作を実行します。また、undoメソッドを使って操作の取り消しを行います。
  4. RemoteControlクラス(インボーカー)

    • RemoteControlクラスは、コマンドを呼び出す役割を持つインボーカークラスです。このクラスは、setCommandメソッドでコマンドをセットし、pressButtonメソッドでコマンドを実行し、pressUndoメソッドで操作を取り消します。
  5. 実際の使用例

    • RemoteControlを使って、ライトを点けたり消したりする操作を行います。pressButtonでコマンドを実行し、pressUndoで操作を取り消すことができます。

利点

  • 疎結合の実現: コマンドの実行を、リクエストを出す側(インボーカー)と実際に実行する側(レシーバー)から分離することで、疎結合を実現します。
  • 操作の履歴管理: コマンドをオブジェクトとして扱うことで、操作の履歴を管理したり、取り消し(アンドゥ)を実装するのが容易になります。
  • 柔軟な拡張性: 新しい操作を追加する際に、既存のコードに大きな変更を加えることなく新しいコマンドを追加できます。

使用例

  • GUI操作の抽象化: GUIのボタン操作やメニューアイテムの選択をコマンドとして扱うことで、動作を抽象化しやすくします。
  • アンドゥ/リドゥの実装: アンドゥやリドゥ機能を実装する際に、コマンドの履歴を管理することで、操作の取り消しや再実行を簡単に行うことができます。
  • タスクのキューイング: コマンドをキューに入れて順次実行することで、複数のタスクを柔軟に管理できます。

まとめ

コマンドパターンは、操作をオブジェクトとしてカプセル化し、呼び出し元から分離することで、柔軟に操作を管理するための強力なパターンです。TypeScriptでの実装を通じて、操作の実行、取り消し、呼び出しの抽象化の仕組みを理解できました。

このパターンを活用することで、特にユーザーインターフェース操作の抽象化や操作の履歴管理が求められるシステムにおいて、保守性と拡張性を高めることができます。