Menu
Coddy logo textTech

コマンドパターン

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

コマンドパターンは、リクエストを、そのリクエストに関するすべての情報を含む独立したオブジェクトに変換する振る舞いに関するデザインパターンです。この変換により、リクエストをメソッドの引数として渡したり、リクエストの実行を遅延させたりキューに入れたり、取り消し可能な操作をサポートしたりできるようになります。

レストランに例えて考えてみましょう。シェフに何を料理するか直接伝える代わりに、注文を伝票に書き込みます。ウェイター(invoker)がその伝票(command)を厨房に運び、そこで注文はキューに入れられたり、優先順位を付けられたり、あるいはキャンセルされたりします。このパターンは、操作を呼び出すオブジェクトと、実際に操作を実行するオブジェクトを分離(デカップル)します。

このパターンには4つの主要なコンポーネントがあります:executeメソッドを宣言するCommandインターフェース、具体的なアクションを実装するConcreteCommandクラス、実際の作業を実行するReceiver、そしてコマンドをトリガーするInvokerです:

interface Command {
    void execute();
}
class Light {  // レシーバー
    public void turnOn() {
        System.out.println("Light is ON");
    }
    public void turnOff() {
        System.out.println("Light is OFF");
    }
}
class LightOnCommand implements Command {
    private Light light;
    
    public LightOnCommand(Light light) {
        this.light = light;
    }
    
    public void execute() {
        light.turnOn();
    }
}
class RemoteControl {  // 呼び出し側 (Invoker)
    private Command command;
    
    public void setCommand(Command command) {
        this.command = command;
    }
    
    public void pressButton() {
        command.execute();
    }
}

インボーカーはどのアクションが実行されるかを知りません。単に execute() を呼び出すだけです:

Light light = new Light();
Command lightOn = new LightOnCommand(light);

RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton();  // 出力: ライトが点灯しました

コマンドパターンは、操作をキューに入れたり、元に戻す/やり直す(undo/redo)機能を実装したり、後で実行するためにタスクをスケジュールしたりする必要がある場合に非常に有効です。各コマンドはアクションの実行に必要なすべての情報をカプセル化するため、システムの柔軟性と拡張性が向上します。

challenge icon

チャレンジ

簡単

Commandパターンを使用して、スマートホームオートメーションシステムを構築しましょう!さまざまな家電デバイスをコマンドを通じて制御できるシステムを作成します。これにより、ユニバーサルリモコンが各デバイスの内部動作を知る必要がないのと同様に、アクションをキューに入れたり、実行したり、後で使用するために保存したりすることが可能になります。

コードは以下の4つのファイルに分けて構成します:

  • Command.java: すべてのデバイスコマンドが実装する Command インターフェースを定義します。コマンドのアクションを実行する単一のメソッド execute() を宣言する必要があります。
  • Devices.java: スマートホームデバイスを表す2つのレシーバークラスを作成します:

    Television - TV is now ON と出力する powerOn() メソッド、TV is now OFF と出力する powerOff() メソッド、および TV channel set to [channel] と出力する setChannel(int channel) メソッドを持ちます。

    Thermostat - Thermostat set to [temp] degrees と出力する setTemperature(int temp) メソッド、および Thermostat turned OFF と出力する turnOff() メソッドを持ちます。

  • Commands.java: デバイスの操作をカプセル化する具体的なコマンドクラスを作成します:

    TVOnCommand - コンストラクタで Television を受け取り、実行時に powerOn() を呼び出します。

    TVOffCommand - コンストラクタで Television を受け取り、実行時に powerOff() を呼び出します。

    TVChannelCommand - コンストラクタで Televisionint channel を受け取り、実行時にそのチャンネルで setChannel() を呼び出します。

    ThermostatSetCommand - コンストラクタで Thermostatint temperature を受け取り、実行時にその値で setTemperature() を呼び出します。

  • Main.java: コマンドを保存するための setCommand(Command cmd) メソッドと、保存されたコマンドを実行するための pressButton() メソッドを持つ RemoteControl クラス(インボーカー)を作成します。

    mainメソッドでは、TVのチャンネル番号(整数)とサーモスタットの温度(整数)の2つの入力を受け取ります。

    TelevisionThermostat を作成します。RemoteControl を作成し、以下の手順でCommandパターンのデモンストレーションを行います:

    1. TVOnCommand をセットしてボタンを押す
    2. 入力されたチャンネルで TVChannelCommand をセットしてボタンを押す
    3. 入力された温度で ThermostatSetCommand をセットしてボタンを押す
    4. TVOffCommand をセットしてボタンを押す

入力は、TVのチャンネル(整数)とサーモスタットの温度(整数)の順に2つ与えられます。

例えば、入力が 572 の場合、出力は以下のようになります:

TV is now ON
TV channel set to 5
Thermostat set to 72 degrees
TV is now OFF

RemoteControl がデバイスと直接やり取りすることはなく、Command インターフェースのみを認識していることに注目してください。各コマンドはアクションの実行に必要なすべてをカプセル化しているため、インボーカーを変更することなく、新しいデバイスやコマンドを簡単に追加できます!

自分で試してみよう

import java.util.Scanner;

// TODO: 以下の機能を持つ RemoteControl クラス(インボーカー)を作成します:
// - コマンドを保存するための setCommand(Command cmd) メソッド
// - 保存されたコマンドを実行するための pressButton() メソッド

class RemoteControl {
    // TODO: RemoteControl を実装します
}

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // 入力を読み取ります
        int channel = scanner.nextInt();
        int temperature = scanner.nextInt();
        
        // TODO: Television と Thermostat を作成します
        
        // TODO: RemoteControl を作成します
        
        // TODO: Command パターンを実演します:
        // 1. TVOnCommand をセットしてボタンを押します
        // 2. 入力された channel を指定して TVChannelCommand をセットし、ボタンを押します
        // 3. 入力された temperature を指定して ThermostatSetCommand をセットし、ボタンを押します
        // 4. TVOffCommand をセットしてボタンを押します
        
        scanner.close();
    }
}
quiz icon腕試し

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

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