デザインパターン学習メモ:「Command」


このパターンの目的

GoF本より引用する。

要求をオブジェクトとしてカプセル化することによって、異なる要求や、要求からなるキューやログにより、クライアントをパラメータ化する。また、取り消し可能なオペレーションをサポートする。

execute()のみをもつインタフェース(Java言語)を作成し、このインタフェースを実装したコマンド(命令)クラスを作る。
このコマンドクラスを要求として渡された処理の実行者は、その中身を知らなくてもexecute()を呼ぶことで実行することができる。つまり、処理の内部実装のカプセル化である。

さらに、カプセル化を用いて処理を抽象的に扱うことによって、共通の方法で要求の取り消しや再実行可能にする。
この場合は、状態の保存が必要になる。

実装例

『アジャイルソフトウェア開発の奥義』(P.203)をベースに作成。Commandパターンを利用したActive Objectパターンとして紹介されている。

Command.java
// Command役
public interface Command {
    public void execute();
}
ActiveObjectEngine.java
// Invoker役
public class ActiveObjectEngine {
    LinkedList<Command> itsCommands = new LinkedList<Command>();

    public void addCommand(Command c) {
        itsCommands.add(c);
    }

    // リストからコマンドを取り出し、一つずつ実行する
    public void run() {
        while(!itsCommands.isEmpty()) {
            Command c = itsCommands.getFirst();
            itsCommands.removeFirst();
            c.execute();
        }
    }
}
SimpleCommand.java
// ConcreteCommand役
public class SimpleCommand implements Command {
    private String name;

    public SimpleCommand(String name) {
        this.name = name;
    }

    @Override
    public void execute() {
        System.out.println("Executed by " + name);
    }
}
SleepCommand.java
// ConcreteCommand役
// 自身の処理が完了した後に、wakeUpCommandをEngineに登録する。
public class SleepCommand implements Command {
    private String name;
    private Command wakeUpCommand;
    private ActiveObjectEngine engine;
    private long sleepTimeMilliSec;
    private long startTime;
    private boolean started = false;

    public SleepCommand(String name, long sleepTimeMilliSec,
            ActiveObjectEngine engine, Command wakeUpCommand) {
        this.name = name;
        this.sleepTimeMilliSec = sleepTimeMilliSec;
        this.engine = engine;
        this.wakeUpCommand = wakeUpCommand;
    }

    @Override
    public void execute() {
        long currentTime = System.currentTimeMillis();
        if (!started) {
            System.out.println(name + " started.");
            started = true;
            startTime = currentTime;
            engine.addCommand(this);
        } else if ((currentTime - startTime) < sleepTimeMilliSec) {
            engine.addCommand(this);
        } else {
            System.out.println("Executed by " + name);
            engine.addCommand(wakeUpCommand);
        }
    }
}

実行例

java.Main.java
public class Main {
    public static void main(String[] args) {
        ActiveObjectEngine engine = new ActiveObjectEngine();
        engine.addCommand(new SimpleCommand("SimpleCommand-A"));
        engine.addCommand(new SimpleCommand("SimpleCommand-B"));
        engine.addCommand(new SleepCommand("SleepCommand-C", 3 * 1000, engine,
                new SimpleCommand("WakeupCommand-C")));
        engine.addCommand(new SimpleCommand("SimpleCommand-D"));

        engine.run();
    }
}
結果
Executed by SimpleCommand-A
Executed by SimpleCommand-B
SleepCommand-C started.
Executed by SimpleCommand-D
Executed by SleepCommand-C
Executed by WakeupCommand-C

基本的に順番通りに各Commandが実行されている。SleepCommandだけは、意図通りSleepしているため、完了が最後になっている。

参考文献

  • エリック ガンマ、ラルフ ジョンソン、リチャード ヘルム、ジョン プリシディース(1999)『オブジェクト指向における再利用のためのデザインパターン 改訂版』本位田 真一、吉田 和樹 監訳、SBクリエイティブ
  • ロバート・C・マーチン(2004)『アジャイルソフトウェア開発の奥義 第2版 オブジェクト指向開発の神髄と匠の技』瀬谷啓介訳、SBクリエイティブ