命令模式是我們能夠實現發送者和接收者之間的完全解耦,發送者是調用操作的對象,而接收者是接收請求並執行特定操作的對象。通過解耦,發送者無需瞭解接收者的介面。在這裡,請求的含義是需要被執行的命令。 作用 將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數化;對請求排隊或記錄請求日誌,以及支持 ...
命令模式是我們能夠實現發送者和接收者之間的完全解耦,發送者是調用操作的對象,而接收者是接收請求並執行特定操作的對象。通過解耦,發送者無需瞭解接收者的介面。在這裡,請求的含義是需要被執行的命令。
作用
將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數化;對請求排隊或記錄請求日誌,以及支持可撤銷的操作。當將客戶的單個請求封裝成對象以後,我們就可以對這個請求存儲更多的信息,使請求擁有更多的能力;命令模式能夠把請求發送者和接收者解耦,使得命令發送者不用去關心請求將以何種方式被處理。
類視圖
實現
class Command;
/*調用者(Invoker)*/
class Switch
{
public:
Switch(){}
~Switch(){}
void insertcommand(Command* pCom)
{
m_command.pus_back(pCom);
}
void excuteonce()
{
if (m_command.empty())
return;
m_command.front()->excute();
m_command.pop_front();
}
private:
list< share_prt<Command*> > m_command;
};
/*接收者(Recever)*/
class Appliance
{
public:
Appliance(){}
virtual ~Appliance(){}
virtual bool start() = 0;
virtual bool stop() = 0;
};
class AirConditioner : public Appliance
{
public:
AirConditioner(){}
virtual ~AirConditioner(){}
bool start()
{
cout <<"the air conditioner is on" << endl;
}
bool stop()
{
cout <<"the air conditioner is off" << endl;
}
};
class Fridge : public Appliance
public:
Fridge(){}
virtual ~Fridge(){}
bool On()
{
cout <<"the firdge is on" << endl;
}
bool Off()
{
cout <<"the firdge is off" << endl;
}
};
/*命令(Command)*/
class Command
{
public:
Command(Appliance* pAl): m_pAlice(pAl){}
virtual ~Command(){}
virtual excute();
protected:
Appliance* m_pAlice;
};
class OnCommand : public Command
{
public:
OnCommand(Appliance* pAl): Command(pAl){}
virtual ~OnCommand(){}
bool excute()
{
m_pAlice.On();
}
};
class OffCommand : public Command
{
public:
OffCommand(Appliance* pAl): Command(pAl){}
virtual ~OffCommand(){}
bool excute()
{
m_pAlice.Off();
}
};
int main()
{
//接收者
AirConditioner air;
Fridge fridge;
//生成命令
OnCommand On1(&air);
OnCommand On2(&fridge);
OffCommand off1(&air);
OffCommand off2(&fridge);
//發送命令
Switch switcher;
switcher.insertcommand(&on1);
switcher.insertcommand(&on2);
switcher.insertcommand(&off1);
switcher.insertcommand(&off2);
//執行命令
switcher.excuteonce();
switcher.excuteonce();
switcher.excuteonce();
switcher.excuteonce();
}
從如上的例子中可以分析出,調用者switch不會關心是開命令還是關命令,是屬於空調的命令還是冰箱的命令,這樣一方面可以做到解耦的效果,另一方面,還可以對接收者和命令進行方便的擴展,這就是命令模式的核心優點所在。當然缺點也正是如此,接收者和命令的組合數量是MxN的關係,再擴展是需要註意數量級的大小。
應用場景
- 通過==參數化對象==實現功能執行,命令是面向對象式的,而不是回調函數式的;
- ==指定消息隊列併在不同時間執行請求==。一個命令對象可以有獨立於原始請求的生命周期。如果一個請求的接收者可以由一個獨立地址空間的方式來表示,那麼你可以將請求對象的命令轉換到不同的進程空間併在其中完成請求。
- ==支持撤銷==。命令的執行操作可以作為狀態進行存儲,併在需要時實現命令撤銷,命令的介面必須增加一個unexcude操作,進行撤銷操作的執行。
- ==支持日誌記錄變化==,在系統崩潰的情況下使用命令可以重新應用。通過增加load和store操作命令介面,可以保存一個持續變化的日誌,從系統崩潰中恢復,需要重新載入日誌命令和使用excute操作重新執行這些命令。
- ==通過在原生操作基礎上的高層操作構建系統==。這樣的結構在支持交易操作的系統中很常見。一個交易事物封裝一組變化的數據,命令模式提供了一種交易模型,命令都有一個共同的介面,允許你使用相同的方式調用所有的交易。這種模式也使得它很容易與新的交易系統進行交互擴展。