Menu
Coddy logo textTech

コマンドパターン

CoddyのDartジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 94/110。

Commandパターンは、リクエストをオブジェクトとしてカプセル化することで、異なるリクエストによるクライアントのパラメータ化、操作のキューイング、または「元に戻す」機能のサポートを可能にします。メソッドを直接呼び出す代わりに、アクションをコマンドオブジェクトにラップし、それを保存したり、受け渡したり、後で実行したりできるようにします。

このパターンは、3つの主要なコンポーネントで構成されています。executeメソッドを持つCommandインターフェース、具体的なアクションを実装するConcrete Commands、そして実際の処理を行うReceiverです。オプションのInvokerは、コマンドの内容を知ることなく、コマンドを実行します。

abstract class Command {
  void execute();
}

class Light {
  void turnOn() => print('Light is ON');
  void turnOff() => print('Light is OFF');
}

class TurnOnCommand implements Command {
  final Light light;
  TurnOnCommand(this.light);

  @override
  void execute() => light.turnOn();
}

class TurnOffCommand implements Command {
  final Light light;
  TurnOffCommand(this.light);

  @override
  void execute() => light.turnOff();
}

class RemoteControl {
  Command? _command;

  void setCommand(Command command) {
    _command = command;
  }

  void pressButton() {
    _command?.execute();
  }
}

void main() {
  var light = Light();
  var remote = RemoteControl();

  remote.setCommand(TurnOnCommand(light));
  remote.pressButton();  // Light is ON

  remote.setCommand(TurnOffCommand(light));
  remote.pressButton();  // Light is OFF
}

RemoteControl はライトについて何も知りません。与えられたコマンドを実行するだけです。この疎結合により、インボーカー(起動側)を修正することなく、新しいコマンドを簡単に追加できるようになります。Command パターンは、アクションを第一級オブジェクトとして扱う必要がある、元に戻す/やり直し(undo/redo)システム、タスクキュー、またはマクロ記録の実装に最適です。

challenge icon

チャレンジ

簡単

Command パターンを使用して、テキストエディタのコマンドシステムを構築しましょう!テキスト操作をコマンドオブジェクトとしてカプセル化するシステムを作成します。これにより、エディタは各操作の詳細を知ることなく、さまざまなアクションを実行できるようになります。これは Command パターンの典型的なユースケースです。各アクションがオブジェクトとして保存される、元に戻す/やり直し(undo/redo)システムのようなものだと考えてください。

コードを 2 つのファイルに整理します。

  • commands.dart: ここにコマンドシステムを作成します。まず、execute() メソッドを宣言する抽象クラス Command から始めます。次に、String content フィールド(初期状態は空)を持ち、以下の 3 つのメソッドを持つ TextDocument クラス(レシーバ)を作成します。
    • append(String text) - content の末尾にテキストを追加します
    • clear() - content を空の文字列に設定します
    • show() - Document: [content] を出力します
    次に、TextDocument を操作する 3 つの具体的なコマンドを実装します。
    • AppendCommand - コンストラクタでドキュメントとテキストを受け取り、実行時にテキストを追加します
    • ClearCommand - コンストラクタでドキュメントを受け取り、実行時に内容をクリアします
    • ShowCommand - コンストラクタでドキュメントを受け取り、実行時にその内容を表示します
    最後に、コマンドのリストを保持する TextEditor クラス(インボーカー)を作成します。コマンドをキューに追加するための addCommand(Command command) メソッドと、キューに入れられたすべてのコマンドを順番に実行する runAll() メソッドを持つ必要があります。
  • main.dart: コマンドファイルをインポートし、Command パターンを実演します。TextDocumentTextEditor を作成します。次のコマンドを順番にキューに追加します:Hello を追加、 World を追加、ドキュメントを表示、! を追加、再度表示、ドキュメントをクリア、そして最後に一度表示します。その後、キューに入れられたすべてのコマンドを実行します。

TextEditor が追加、クリア、表示について何も知らないことに注目してください。与えられたコマンドを実行するだけです。この疎結合こそが Command パターンの本質です!

期待される出力:

Document: Hello World
Document: Hello World!
Document: 

自分で試してみよう

import 'commands.dart';

void main() {
  // TODO: TextDocument のインスタンスを作成する

  // TODO: TextEditor のインスタンスを作成する

  // TODO: 以下のコマンドを順番にキューに追加する:
  // 1. "Hello" を追加する
  // 2. " World" を追加する
  // 3. ドキュメントを表示する
  // 4. "!" を追加する
  // 5. ドキュメントを再度表示する
  // 6. ドキュメントをクリアする
  // 7. ドキュメントを最後に一度表示する

  // TODO: キューに入れられたすべてのコマンドを実行する
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン