適用問題:
命令模式最好的體現就是遙控器了,遙控器上有許多按鍵,透過按鍵我們可以要求某物件執行命令,例如我們按下有開燈功能的按鍵,燈泡就會亮這樣。命令模式適用的情況通常存在請求者與執行者兩個角色,以餐廳為例,客人與廚師分別就代表請求者與執行者,而餐廳裡的服務生就好比是遙控器,客人向服務生點餐,服務生再傳遞點餐內容給廚師,客人須透過服務生這個角色間接傳遞指令給廚師,這樣的安排是比起客人直接向廚師點餐更加彈性,可以作到像是服務生等收集多一點的點菜單後才一起告知廚房。
命令模式將請求封裝成命令物件,方便我們做到照順序執行之類的需求,管理上有許多好處。
命令模式定義:
"The Command Pattern encapsulates a request as an object, thereby letting you parameterize other objects with different requests, queue or log requests, and support undoable operations." (將一個請求封裝成一個物件,讓你可用不同的請求對客戶進行參數化、對請求排隊或記錄請求日誌,以及支援可取消的操作)
命令模式UML:
Receiver:Receiver知道如何完成工作處理請求。(電燈泡 / 廚師)
Invoker:持有命令,可呼叫命令的execute()。(遙控器 / 服務生)
命令模式code(以燈泡為例)(C++):
class Command{
public:
virtual void execute() = 0;
};
class Light{
public:
void on(){ cout<<"The light is on\n"; }
void off(){ cout<<"The light is off\n"; }
};
class LightOnCommand : public Command{
private:
Light *mLight;
public:
LightOnCommand(Light *light):mLight(light){}
void execute(){
mLight->on();
}
}
class LightOffCommand : public Command{
private:
Light *mLight;
public:
LightOffCommand(Light *light):mLight(light){}
void execute(){
mLight->off();
}
}
class RemoteControl{
private:
Command *mCmd;
public:
void setCommand(Command *cmd){
mCmd = cmd;
}
void buttonPressed(){
mCmd->execute();
}
}
int main() {
Light *light = new Light;
LightOnCommand *lightOn = new LightOnCommand(light);
LightOffCommand *lightOff = new LightOffCommand(light);
RemoteControl *control = new RemoteControl;
control->setCommand(lightOn);
control->buttonPressed();
control->setCommand(lightOff);
control->buttonPressed();
delete light, lightOn, lightOff, control;
return 0;
}
上面這個例子是遙控器上只有一個按鍵,因此要一直重setCommand,若想要多按鍵,可以修改 class RemoteControl的mCmd成 vector之類能裝複數東西的容器,其他函式則要做點調整增加能指定是要下第幾個command用的參數。
Command的初始化建議初始化成下面這個什麼也不會執行的命令。
class NoCommand : public Command{
public:
void execute(){}
}
命令模式的一些應用:
1. queuing requests:設計一佇列內依序排許多request commands,空閒的thread會從佇列的前頭提取一個命令,並執行其execute()函式。
2. logging request:為了因應當機中斷執行,我們需要log存下action()動作以利恢復,作法是在命令物件加入save()與store()函式。 (但這個問題好像現在很少這麼解決,而是改建立一個介面Logger內有 void writeFile(String pathName, Object object) 與 Object readFile(String pathName) 兩個函式)
參考資料:
1. Head First Design Patterns
2. code來源:https://www.bogotobogo.com/DesignPatterns/command.php
沒有留言:
張貼留言