Menu
Coddy logo textTech

コマンドパターン

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

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

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

このパターンには4つの主要なコンポーネントがあります。Command interface(execute methodを宣言する)、特定のactionsを実装するConcreteCommand classes、実際の処理を実行するレシーバー、そしてcommandsを呼び出す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 {  // 起動者
    private Command command;
    
    public void setCommand(Command command) {
        this.command = command;
    }
    
    public void pressButton() {
        command.execute();
    }
}

invoker はどのアクションが実行されるかを知りません。単に execute() を呼び出します。

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

RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton();  // 出力: Light is ON

コマンドパターンは、操作をキューに追加したり、undo/redo機能を実装したり、後で実行するタスクをスケジュールしたりする必要がある場合に特に優れています。各コマンドは、アクションを実行するために必要なすべてをカプセル化するため、システムの柔軟性と拡張性が高まります。

challenge icon

チャレンジ

簡単

Command Patternを使用してスマートホーム自動化システムを構築しましょう!さまざまな家庭用deviceをcommandsを通して制御できるシステムを作成します。これにより、universal remote controlのように、各deviceが内部でどのように動作するかを知る必要なく、actionsをキューに入れたり、実行したり、後で使用するために保存したりできます。

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

  • Command.java: すべてのdevice commandsが実装するCommand interfaceをDefineします。commandのactionを実行する単一のmethod execute()をDeclareする必要があります。
  • Devices.java: スマートホームのdeviceを表す2つのreceiver classesをCreateします。

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

    Thermostat - Thermostat set to [temp] degreesを出力するsetTemperature(int temp)と、Thermostat turned OFFを出力するturnOff() methodsを持ちます。

  • Commands.java: device operationsをencapsulateするConcrete command classesをCreateします。

    TVOnCommand - constructorでTelevisionを受け取り、executedされるとpowerOn()をCallsします。

    TVOffCommand - constructorでTelevisionを受け取り、executedされるとpowerOff()をCallsします。

    TVChannelCommand - constructorでTelevisionint channelを受け取り、executedされるとそのchannelを指定してsetChannel()をCallsします。

    ThermostatSetCommand - constructorでThermostatint temperatureを受け取り、executedされるとそのvalueを指定してsetTemperature()をCallsします。

  • Main.java: commandを保存するsetCommand(Command cmd) methodと、保存されたcommandをexecuteするpressButton() methodを持つRemoteControl class(invoker)をCreateします。

    main methodでは、2つのinputs、つまりTV channel number(integer)とthermostat temperature(integer)を受け取ります。

    TelevisionThermostatをCreateします。RemoteControlをCreateし、次の手順でCommand PatternをDemonstrateします。

    1. TVOnCommandを設定してbuttonを押す
    2. input channelを指定してTVChannelCommandを設定し、buttonを押す
    3. input temperatureを指定してThermostatSetCommandを設定し、buttonを押す
    4. TVOffCommandを設定してbuttonを押す

2つのinputsを順番に受け取ります。TV channel(integer)とthermostat temperature(integer)です。

たとえば、inputsが572の場合、outputは次のようになります。

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

RemoteControlがdeviceと直接やり取りすることは決してない点に注目してください。RemoteControlが知っているのはCommand interfaceだけです。各commandはactionの実行に必要なすべてをencapsulateしているため、invokerを変更せずに新しいdeviceやcommandsを簡単に追加できます。

自分で試してみよう

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. 入力チャンネルでTVChannelCommandを設定してボタンを押す
        // 3. Set a ThermostatSetCommand with the input temperature and press the button
        // 4. TVOffCommandを設定してボタンを押す
        
        scanner.close();
    }
}
quiz icon腕試し

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

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

自分で練習してみよう: Javaオンラインコンパイラ