"Allows you to create macros of commands so that you can execute multiple commands at once."
我們可以利用 Meta Command 一次執行很多個命令,延續上篇的範例中,還沒完成的 undo()
功能就是應用這個概念,undo就是 ctrl+z 回到上一步,假如現在執行 LightOffCommand()
關燈,那undo後就會回到LightOnCommand()
開燈的狀態。
前篇程式碼少了一個判斷,如果按下沒設定功能的按鈕會直接報錯XD
所以我們新增一個NoCommand()
類別,在Remote()建構子先把所有按鈕都初始化成 NoCommand()
,另外要把
undoCommand 也先初始化成沒有命令。
一開始可能會直觀地覺得,undo不就是取消現在的動作,以中文理解就是什麼都不做?
為什麼要先記住現在的命令?
其實Undo翻成中文應該要理解成回到上一步,假設現在把燈打開,我們取消動作後,若先前沒有undoCommand這個物件去記憶動作,燈就會保持在打開的狀態,下一次我們再按一下就會把燈關掉,而不是我們想的把燈打開。(完全像是在繞口令)
所以要修改過後的程式碼會變成以下:
首先新增一個類別 NoCommand()
public interface Command{
public void execute();
public void undo();
}
public class NoCommand implements Command{
public void execute(){
System.out.println("The button hasn't been assign to any command.");
}
public void undo(){}
}
修改遙控器類別的初始化、新增Undo按鈕
public class Remote extends Invoker{
ArrayList<Command> onCommands;
ArrayList<Command> offCommands;
Command undoCommand;
public Remote(){
NoCommand noCommand = new NoCommand();
//先把八個按鈕包含undo都初始化成 noCommand
onCommands = new ArrayList<Command>(Collections.nCopies(8, noCommand));
offCommands = new ArrayList<Command>(Collections.nCopies(8, noCommand));
undoCommand = new NoCommand();
}
public void setCommand(int btnVal, Command onCommand, Command offCommand){
onCommands.set(btnVal - 1, onCommand);
offCommands.set(btnVal - 1, offCommand);
}
public void pressOnBtn(int btnVal){
System.out.print(this.toString(btnVal, "ON"));
onCommands.get(btnVal - 1).execute();
//要先讓undo按鈕知道現在執行的命令,如果使用者按下undo,就是執行跟undo記憶的相反的命令
//若等下使用者按下undo,才會讓命令回到Off
undoCommand = onCommands.get(btnVal - 1);
}
public void pressOffBtn(int btnVal){
System.out.print(this.toString(btnVal, "OFF"));
offCommands.get(btnVal - 1).execute();
//若等下使用者按下undo,才會讓命令回到On
undoCommand = offCommands.get(btnVal - 1);
}
public void pressUndoBtn(){
System.out.print(this.toString(0, "UNDO"));
onCommands.get(7).execute();
undoCommand.undo();
}
// 可以顯示目前所有按鈕的功能
@Override
public String toString() {
StringBuffer stringBuf = new StringBuffer();
stringBuf.append("\n_________________ Remote Control _________________\n\n");
for(int i = 0; i < onCommands.size() - 1; i++){
stringBuf.append("[ SLOT "+ (i + 1) + "] " + onCommands.get(i).getClass().getName());
stringBuf.append(" " + "[ SLOT " + (i + 1) + "] " + offCommands.get(i).getClass().getName() + "\n");
}
stringBuf.append("[ SLOT " + (onCommands.size()) + "] " + undoCommand.getClass().getName() + " -> " + "UNDO \n");
return stringBuf.toString();
}
//可以顯示目前按下的按鈕
public String toString(int btnVal, String func){
return "Press " + func + " button " + btnVal + " -> ";
}
}
以開關燈的命令為例,undo()
就是執行相反的令令
public class LightOnCommand implements Command{
Light light;
public LightOnCommand(Light light){
this.light = light;
}
public void execute(){
light.on();
}
public void undo(){
light.off();
}
}
public class LightOffCommand implements Command{
Light light;
public LightOffCommand(Light light){
this.light = light;
}
public void execute(){
light.off();
}
public void undo(){
light.on();
}
}
最後再測試看看:
public class CommandPattern {
public static void main(String[] args) {
Remote remote = new Remote();
Light livingroomLight = new Light("living room");
Light studioLight = new Light("Music Studio");
Curtain bedroomCurtain = new Curtain("bedroom");
Curtain livingroomCurtain = new Curtain("Livingroom");
Fridge kitchenFridge = new Fridge("kitchen");
CurtainOpenCommand livingroomCurtainOpen = new CurtainOpenCommand(livingroomCurtain);
CurtainCloseCommand livingroomCurtainClose = new CurtainCloseCommand(livingroomCurtain);
CurtainOpenCommand bedroomCurtainOpen = new CurtainOpenCommand(bedroomCurtain);
CurtainCloseCommand bedroomCurtainClose = new CurtainCloseCommand(bedroomCurtain);
LightOnCommand livingroomLightOnCommand = new LightOnCommand(livingroomLight);
LightOffCommand livingroomLightOffCommand = new LightOffCommand(livingroomLight);
LightOnCommand studioLightOnCommand = new LightOnCommand(studioLight);
LightOffCommand studioLightOffCommand = new LightOffCommand(studioLight);
FridgeOpenCommand kitchenFridgeOpenCommand = new FridgeOpenCommand(kitchenFridge);
FridgeCloseCommand kitchenFridgeOffCommand = new FridgeCloseCommand(kitchenFridge);
System.out.println(remote);
remote.setCommand(1, livingroomCurtainOpen, livingroomCurtainClose);
remote.setCommand(2, livingroomLightOnCommand, livingroomLightOffCommand);
remote.setCommand(3, kitchenFridgeOpenCommand, kitchenFridgeOffCommand);
remote.setCommand(4, bedroomCurtainOpen, bedroomCurtainClose);
remote.setCommand(5, studioLightOnCommand, studioLightOffCommand);
System.out.println(remote);
remote.pressOffBtn(4);
remote.pressOnBtn(5);
remote.pressUndoBtn();
remote.pressOnBtn(5);
remote.pressOnBtn(1);
remote.pressOffBtn(7);
}
}
輸出結果
Disclaimer
因為讀的是原文版,所以難免會有翻譯詞不達意或是專有名詞上的差異,有錯誤的話歡迎在留言區一起交流!