また取り消し操作をサポートします。
client はリクエストに応じた ConcreteCommand オブジェクトを作成します。
リクエストは client -> Invoker -> ConcreteCommand -> Receiver の順に処理されます。
また、Invoker では Command の保存処理が行われます。
取り消し操作は Invoker に保存された Command を取り出し、同様に処理されます。
void client() {
Invoker invoker = new Invoker();
invoker.execute(new ConcreteCommandA());
invoker.execute(new ConcreteCommandB());
invoker.undo();
}
interface Command {
public void execute(Receiver receiver);
public void undo(Receiver receiver);
}
class ConcreteCommandA implements Command {
public void execute(Receiver receiver) { receiver.actionA(); }
public void undo(Receiver receiver) { receiver.undoA(); }
}
class ConcreteCommandB implements Command {
public void execute(Receiver receiver) { receiver.actionB(); }
public void undo(Receiver receiver) { receiver.undoB(); }
}
class Receiver {
public void actionA() { System.out.println("actionA"); }
public void actionB() { System.out.println("actionB"); }
public void undoA() { System.out.println("undoA"); }
public void undoB() { System.out.println("undoB"); }
}
class Invoker {
private LinkedList<Command> commands = new LinkedList<>();
private Receiver receiver = new Receiver();
public void execute(Command command) {
this.commands.push(command);
command.execute(this.receiver);
}
public void undo() {
this.commands.pop().undo(this.receiver);
}
}
実行結果:actionA
actionB
undoB
サンプルプログラムとして、簡易的なエディタを示します。
テキスト入力、カーソル移動といった操作を Command 化しています。
実行結果:
// 直接実行
EditorReceiver.input(): hello
EditorReceiver.input(): japan
EditorReceiver.backspace(): 5
EditorReceiver.input(): word
EditorReceiver.cursor(): -1
EditorReceiver.input(): l
hello world
// Command パターン
EditorReceiver.input(): hello
EditorReceiver.input(): japan
EditorInvoker.undo(): input[japan]
EditorReceiver.backspace(): 5
EditorInvoker.execute(): remove undo -> input[japan]
EditorReceiver.input(): word
EditorReceiver.cursor(): -1
EditorReceiver.input(): l
hello world
package design_pattern;
import java.util.LinkedList;
import java.util.ListIterator;
/**
* Command
* GoF
* リクエストをオブジェクトにカプセル化することで、リクエストの情報はパラメータされ、
* また取り消し操作をサポートします。
*
* @author 2015/05/28 matsushima
*/
public class CommandSample {
/**
* main
*
* @param args
*/
public static void main(String[] args) {
new CommandSample().client();
}
/**
* Client
*/
void client() {
System.out.println("// 直接実行");
EditorReceiver receiver = new EditorReceiver();
receiver.input("hello ");
receiver.input("japan");
receiver.backspace(5);
receiver.input("word");
receiver.cursor(-1);
receiver.input("l");
System.out.println(receiver.result());
System.out.println("// Command パターン");
EditorInvoker invoker = new EditorInvoker();
invoker.execute(new InputConcreteCommand("hello "));
invoker.execute(new InputConcreteCommand("japan"));
invoker.undo();
invoker.execute(new InputConcreteCommand("word"));
invoker.execute(new CursorConcreteCommand(-1));
invoker.execute(new InputConcreteCommand("l"));
System.out.println(invoker.result());
}
/** エディタのコマンドのインターフェース。 */
public interface EditorCommand {
public void execute(EditorReceiver receiver);
public void undo(EditorReceiver receiver);
}
/** テキスト入力コマンド。 */
public class InputConcreteCommand implements EditorCommand {
private String text;
public InputConcreteCommand(String text) {
this.text = text;
}
public void execute(EditorReceiver receiver) {
receiver.input(this.text);
}
public void undo(EditorReceiver receiver) {
receiver.backspace(this.text.length());
}
public String toString() {
return "input[" + this.text + "]";
}
}
/** カーソル入力コマンド。 */
public class CursorConcreteCommand implements EditorCommand {
private int distance;
public CursorConcreteCommand(int distance) {
this.distance = distance;
}
public void execute(EditorReceiver receiver) {
receiver.cursor(this.distance);
}
public void undo(EditorReceiver receiver) {
receiver.cursor(-this.distance);
}
public String toString() {
return "cursor[" + this.distance + "]";
}
}
/** エディタレシーバ。 */
public class EditorReceiver {
private StringBuilder text = new StringBuilder();
private int cursorX = 0;
public void input(String text) {
System.out.println("EditorReceiver.input(): " + text);
this.text.insert(this.cursorX, text);
this.cursorX += text.length();
}
public void backspace(int distance) {
System.out.println("EditorReceiver.backspace(): " + distance);
this.text.delete(this.cursorX - distance, this.cursorX);
this.cursorX -= distance;
}
public void cursor(int distance) {
System.out.println("EditorReceiver.cursor(): " + distance);
this.cursorX += distance;
}
public String result() {
return this.text.toString();
}
}
/** エディタインボーカ。 */
public class EditorInvoker {
private LinkedList<EditorCommand> commands = new LinkedList<>();
private ListIterator<EditorCommand> commandIt = commands.listIterator();
EditorReceiver receiver = new EditorReceiver();
public void execute(EditorCommand command) {
// undo 済みを削除
while (this.commandIt.hasNext()) {
EditorCommand c = this.commandIt.next();
System.out.println("EditorInvoker.execute(): remove undo -> " + c);
this.commandIt.remove();
}
// command 登録
this.commandIt.add(command);
// command 実行
command.execute(receiver);
}
public void undo() {
// command undo
EditorCommand command = this.commandIt.previous();
System.out.println("EditorInvoker.undo(): " + command);
command.undo(this.receiver);
}
public String result() {
return this.receiver.result();
}
}
}
0 件のコメント:
コメントを投稿