前面提過命令模式的概念,感覺還是挺抽象的,所以書中有更好懂的譬喻,假設今天我們到一個餐廳用餐,首先我們 Client
點餐時會先看菜單選項 ,決定好再叫服務生Invoker
來進行點餐setCommand()
,服務生會把客人的訂單內容 Command
送到廚房 execute()
給廚師 Receiver
去準備餐點,準備好後再由服務生送到我們 Client
手上。
命令模式的精髓在於,廚師不用知道是誰點了A餐,但是身為 Receiver
的他知道如何煮出那個餐點,也就是在命令模式中要如何執行那個功能,相對的,服務生也就是 Invoker
不用知道怎麼煮,只要負責中間的餐點傳遞。
分析一下,我們依據上篇需求對應到命令模式的角色為何:
Client
: 使用者Invoker
: 遙控器setCommand()
: 按鈕本身Command
: 按鈕對應的功能Receiver
: 家電或其他裝置execute()
: 按下按鈕的動作以下用部分程式碼來講解,完整版在這
首先我們的 Receiver
家電 Light()
就像廚師,只有他知道功能要怎麼實行,所以把開關或是其他動作的方法定義在這裡:
abstract public class Receiver {
public String position;
// some action methods here
}
public class Light extends Receiver{
public Light(String position){
this.position = position;
}
public void On(){
System.out.println("Turn on the light in " + position );
}
public void Off(){
System.out.println("Turn on the light in " + position );
}
}
Command負責執行功能,但不知道詳細執行細節:
public interface Command{
public void execute(Receiver receiver);
}
//不同裝置的不同動作(e.g. 電燈開或關)都會實作 Command() 介面
public class LightOnCommand{
public void execute(Light light){
light.On();
}
}
public class LightOffCommand implements Command{
Light light;
public LightOffCommand(Light light){
this.light = light;
}
public void execute(){
light.Off();
}
}
Invoker 遙控器 Remote()
可以設定不同按鈕連結到的功能,但他不知道功能是什麼,只知道包含開 Command onCommand
和關 Command offCommand
public class Remote extends Invoker{
ArrayList<Command> onCommands;
ArrayList<Command> offCommands;
public Remote(){
onCommands = new ArrayList<Command>(8);
offCommands = new ArrayList<Command>(8);
}
//在對應的數字設定開和關的按鈕,不需了解執行細節,只用到 Command() 介面
public void setCommand(int btnVal, Command onCommand, Command offCommand){
onCommands.add(btnVal - 1, onCommand);
offCommands.add(btnVal - 1, offCommand);
}
public void pressOnBtn(int btnVal){
onCommands.get(btnVal - 1).execute();
}
public void pressOffBtn(int btnVal){
offCommands.get(btnVal - 1).execute();
}
}
先測試一下功能:
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);
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);
remote.pressOffBtn(4);
remote.pressOnBtn(5);
remote.pressOnBtn(1);
remote.pressOnBtn(3);
}
}
輸出:
下篇繼續做完 global 的取消功能 →
Disclaimer
因為讀的是原文版,所以難免會有翻譯詞不達意或是專有名詞上的差異,有錯誤的話歡迎在留言區一起交流!