一 定義
命令模式(Command):將一個請求封裝爲一個對象,從而使你可用不同的請求對客戶進行參數化,對請求進行排隊或記錄請求日誌,以及支持可撤銷的操作。
其本質是對命令的抽象和封裝,把通常的命令發起——>接收到命令並執行過程,通過增加一箇中間角色變爲命令發起——>中介負責先接收命令再轉發——>接收到命令再執行過程,簡而言之,就是命令發起者可以將很多命令放進中間角色, 它並不知道功能是如何實現的,只負責轉發給真正的命令執行者。
二 ULM圖
角色說明:
抽象命令類(Command): 聲明執行操作的接口。調用接收者相應的操作,以實現執行的方法Execute。
具體命令類(ConcreteCommand): 創建一個具體命令對象並設定它的接收者。通常會持有接收者,並調用接收者的功能來完成命令要執行的操作。
調用者(Invoker): 要求該命令執行這個請求。通常會持有命令對象,可以持有很多的命令對象。
接收者(Receiver): 知道如何實施與執行一個請求相關的操作。任何類都可能作爲一個接收者,只要它能夠實現命令要求實現的相應功能。
客戶類(Client): 創建具體的命令對象,並且設置命令對象的接收者。真正使用命令的客戶端是從Invoker來觸發執行。
Command模式優點:
1) 降低系統的耦合度:Command模式將調用操作的對象與知道如何實現該操作的對象解耦。
2) Command是頭等的對象。它們可像其他的對象一樣被操縱和擴展。
3) 組合命令:你可將多個命令裝配成一個組合命令,即可以比較容易地設計一個命令隊列和宏命令。一般說來,組合命令是Composite模式的一個實例。
4) 增加新的Command很容易,因爲這無需改變已有的類。
5)可以方便地實現對請求的Undo和Redo。
命令模式的缺點:
使用命令模式可能會導致某些系統有過多的具體命令類。因爲針對每一個命令都需要設計一個具體命令類,因此某些系統可能需要大量具體命令類,這將影響命令模式的使用。
實例1:
燒烤店有烤羊肉串和烤雞翅。對於就餐的客戶來說,只需要告訴服務員需要點什麼,而不關心點的餐怎麼做。
代碼實現:
#include <iostream>
#include <string>
#include <list>
//Receiver:此處爲烤羊肉串者
class Barbecuer
{
public:
void BakeMutton()
{
std::cout << "烤羊肉串嘍!" << std::endl;
}
void BakeChickenWing()
{
std::cout << "烤雞翅嘍!" << std::endl;
}
};
//Command類,抽象命令
class Command
{
protected:
Barbecuer* receiver;
public:
Command(Barbecuer* receiver)
{
this->receiver = receiver;
}
virtual void ExcuteCommand() = 0;
};
//ConcreteCommand類,具體命令
class BakeMuttonCommand :public Command
{
public:
BakeMuttonCommand(Barbecuer* receiver) :Command(receiver) {}
void ExcuteCommand()
{
receiver->BakeMutton();
}
};
//ConcreteCommand類,具體命令
class BakeChickenWingCommand :public Command
{
public:
BakeChickenWingCommand(Barbecuer* receiver) :Command(receiver) {}
void ExcuteCommand()
{
receiver->BakeChickenWing();
}
};
//Invoker:此處爲Waiter服務員
class Waiter
{
private:
std::list<Command*>* orders;
public:
Waiter()
{
orders = new std::list<Command*>;
}
~Waiter()
{
delete orders;
}
//設置訂單
void SetOrder(Command* command)
{
//判斷命令的類型並分別做不同的處理
if (typeid(*command) == typeid(BakeChickenWingCommand))
{
std::cout << "日誌:服務員:雞翅沒有了,請點別的燒烤!" << std::endl;
}
else if (typeid(*command) == typeid(BakeMuttonCommand))
{
orders->push_back(command);
std::cout << "日誌:增加訂單:命令模式.烤羊肉串 " << std::endl;
}
else
{
std::cout << "日誌:暫時沒有該服務!" << std::endl;
}
}
//通知全部執行
void Notify()
{
std::list<Command*>::iterator it;
for (it = orders->begin(); it != orders->end(); it++)
{
(*it)->ExcuteCommand();
}
}
//取消訂單,這裏僅僅是爲了演示,真實需要根據訂單號來取消
void CancelOrder(Command* command)
{
if (typeid(*command) == typeid(BakeChickenWingCommand))
{
orders->remove_if([](const auto& item) { return typeid(*item) == typeid(BakeChickenWingCommand); });
std::cout << "取消訂單:烤雞翅" << std::endl;
}
else if (typeid(*command) == typeid(BakeMuttonCommand))
{
orders->remove_if([](const auto& item) { return typeid(*item) == typeid(BakeChickenWingCommand); });
std::cout << "取消訂單:烤羊肉串" << std::endl;
}
}
};
int main()
{
//開店前的準備
Barbecuer* boy = new Barbecuer();
Command* bakeMuttonCommand = new BakeMuttonCommand(boy);
Command* bakeChickenWingCommand = new BakeChickenWingCommand(boy);
Waiter* girl = new Waiter();
//開門營業,顧客點菜
girl->SetOrder(bakeMuttonCommand);
girl->SetOrder(bakeChickenWingCommand);
girl->CancelOrder(bakeChickenWingCommand);
//點菜完畢,通知廚房
girl->Notify();
delete boy, bakeMuttonCommand, bakeMuttonCommand, bakeChickenWingCommand;
return 0;
}
運行結果:
實例2:
電視機遙控器 :
電視機是請求的接收者, 遙控器是請求的發送者, 遙控器上有一些按鈕,不同的按鈕對應電視機的不同操作。抽象命令角色由一個命令接口來扮演, 有三個具體的命令類實現了抽象命令接口,這三個具體命令類分別代表三種操作:打開電視機、關閉電視機和切換頻道。
顯然,電視機遙控器就是一個典型的命令模式應用實例。
ULM圖:
代碼實現:
#include <iostream>
#include <memory>
//Receiver: Television
class Television
{
public:
void open()
{
std::cout << "打開電視機!" << std::endl;
}
void close()
{
std::cout << "關閉電視機!" << std::endl;
}
void changeChannel()
{
std::cout << "切換電視頻道!" << std::endl;
}
};
//AbstartCommand
class AbstractCommand
{
public:
virtual void execute() = 0;
};
//Concrete commmand: TVOpenCommand
class TVOpenCommand : public AbstractCommand
{
public:
TVOpenCommand(std::shared_ptr<Television> tv)
{
this->tv = tv;
}
void execute()
{
tv->open();
}
private:
std::shared_ptr<Television> tv;
};
//Concrete command: TVCloseCommmand
class TVCloseCommand : public AbstractCommand
{
public:
TVCloseCommand(std::shared_ptr<Television> tv)
{
this->tv = tv;
}
void execute()
{
tv->close();
}
private:
std::shared_ptr<Television> tv;
};
//Concrete command: TVChangeCommmand
class TVChangeCommand : public AbstractCommand
{
public:
TVChangeCommand(std::shared_ptr<Television> tv)
{
this->tv = tv;
}
void execute()
{
tv->changeChannel();
}
private:
std::shared_ptr<Television> tv;
};
//Invoker: Controller
class Controller
{
public:
Controller(std::shared_ptr<AbstractCommand> openCommand,
std::shared_ptr<AbstractCommand> closeCommand,
std::shared_ptr<AbstractCommand> changeCommand) {
this->openCommand = openCommand;
this->closeCommand = closeCommand;
this->changeCommand = changeCommand;
}
void open()
{
openCommand->execute();
}
void change()
{
changeCommand->execute();
}
void close()
{
closeCommand->execute();
}
private:
std::shared_ptr<AbstractCommand> openCommand;
std::shared_ptr<AbstractCommand> closeCommand;
std::shared_ptr<AbstractCommand> changeCommand;
};
//客戶端測試
int main(void) {
// 接收者電視機
std::shared_ptr<Television> tv = std::make_shared<Television>();
// 命令
std::shared_ptr<AbstractCommand> openCommand = std::make_shared<TVOpenCommand>(tv);
std::shared_ptr<AbstractCommand> closeCommand = std::make_shared<TVCloseCommand>(tv);
std::shared_ptr<AbstractCommand> changeCommand = std::make_shared<TVChangeCommand>(tv);
// 調用者
std::shared_ptr<Controller> controll = std::make_shared<Controller>(openCommand, closeCommand, changeCommand);
// 測試
controll->open();
controll->change();
controll->close();
return 0;
}
運行結果: