Command パターン (命令, 操作をクラス (オブジェクト) で表現し, そのオブジェクトを切り替えるおで操作の切り替えを実現する)

[Command] = [命令]

このパターンでは, 一つ若しくは複数の命令を一つのオブジェクトで表現 (命令の詳細処理をカプセル化) する. また, 命令をオブジェクトとして管理するため, その命令の履歴管理, UNDO (取り消し) 機能の実装が容易に行える.

役割

  1. Command (命令):
命令のインタフェースを定義する
  1. ConcreteCommand[A|B] (具体的な命令):
[Command] のインタフェースを実装する
  1. Receiver (受信者):
[Command] の処理対象となるオブジェクトのインタフェース
  1. ConcreteReceiver (具体的な受信者):
[Receiver] のインタフェースを実装する. このように, [Receiver] インタフェースを介することで, 複数の命令の受取手 ([ConcreteReceiver]) を作成することができる.
  1. Invoker (起動者):
[Command] で定義されているインタフェースを呼び出す. また, 複数の [ConcreteCommand] を保持することにより, 命令の履歴管理機能, UNDO 機能等を提供する.
  1. Client (利用者):
ConcreteCommand[A|B] の初期設定 ([ConcreteReceiver] を命令の受取手としてセット), それらの命令を格納した [Invoker] の起動を行う. [Command] パターンを適用したクラスを利用し処理する.

クラス図

Command パターンのクラス図

_images/designpattern-command011.gif

ソースコード

  1. Command.java
1
2
3
4
public abstract class Command{
    public void setReceiver(Receiver receiver){}
    public abstract void execute();
}

2-1. ConcreteCommandA.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class ConcreteCommandA extends Command{
    private int id;
    protected Receiver receiver;
    private final String name = "A";

    public ConcreteCommandA(int id){
	this.id = id;
    }

    public void setReceiver(Receiver receiver){
	this.receiver = receiver;
    }

    public void execute(){
	receiver.action(name + ":" + id);
    }
}

2-2. ConcreteCommandB.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class ConcreteCommandB extends Command{
    private int id;
    protected Receiver receiver;
    private final String name = "B";

    public ConcreteCommandB(int id){
	this.id = id;
    }

    public void setReceiver(Receiver receiver){
	this.receiver = receiver;
    }

    public void execute(){
	receiver.action(name + ":" + id);
    }
}
  1. Receiver.java
1
2
3
public interface Receiver{
    public abstract void action(String msg);
}
  1. ConcreteReceiver.java
1
2
3
4
5
public class ConcreteReceiver implements Receiver{
    public void action(String msg){
	System.out.println(msg);
    }
}
  1. Invoker.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.util.Iterator;
import java.util.Stack;

public class Invoker{
    private Stack<Command> commands = new Stack<Command>();

    public void addCommand(Command command){
	commands.push(command);
    }

    public void undoCommand(){
	Command command = (Command)commands.pop();
	System.out.println();
	System.out.println("undo - 削除された命令 DOWN");
	command.execute();
	System.out.println("undo - 削除された命令 UP");
	System.out.println();
    }

    public void execute(){
	Iterator<Command> it = commands.iterator();
	while(it.hasNext()){
	    it.next().execute();
	}
    }
}
  1. Client.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Client{
    public static void main(String[] args){
	Command[] commandsA = new Command[5];
	Command[] commandsB = new Command[5];

	Receiver receiver = new ConcreteReceiver();
	Invoker invoker = new Invoker();

	for(int i = 0; i < commandsA.length; i++){
	    commandsA[i] = new ConcreteCommandA(i);
	    commandsA[i].setReceiver(receiver);
	    invoker.addCommand(commandsA[i]);
	}

	for(int i = 0; i < commandsB.length; i++){
	    commandsB[i] = new ConcreteCommandB(i);
	    commandsB[i].setReceiver(receiver);
	    invoker.addCommand(commandsB[i]);
	}

	invoker.execute();
	invoker.undoCommand();
	invoker.execute();
    }
}

上記のプログラムの実行結果:

[wtopia Command]$ java Client
A:0
A:1
A:2
A:3
A:4
B:0
B:1
B:2
B:3
B:4

undo - 削除された命令 DOWN
B:4
undo - 削除された命令 UP

A:0
A:1
A:2
A:3
A:4
B:0
B:1
B:2
B:3