Command命令模式是一種“行爲變化”模式
- 在組件的構建過程中,組件行爲的變化經常導致組件本身劇烈的變化。“行爲變化”模式將組件的行爲和組件本身進行解耦,從而支持組件行爲的變化,實現兩者之間的松耦合
動機
- 在軟件構建過程中,“行爲請求者”與“行爲實現者”通常呈現出一種“緊耦合”。但在某些場合——比如需要對行爲進行“記錄/撤銷/重(undo/redo)/事務”等處理,這種無法抵禦變化的緊耦合是不合適的
- 在這種情況下,如何將“行爲請求者”與“行爲實現者”解耦?將一組行爲抽象爲對象,可以實現兩者之間的松耦合
定義
- 將一個請求(行爲)封裝爲一個對象,從而使你可以用不同的請求對客戶進行參數化;對請求排隊或記錄請求日誌,以及支持可撤銷的操作
結構
代碼對比
command.cpp
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Command
{
public:
virtual void execute() = 0;
};
class ConcreteCommand1 : public Command
{
string arg;
public:
ConcreteCommand1(const string & a) : arg(a) {}
void execute() override
{
cout<< "#1 process..."<<arg<<endl;
}
};
class ConcreteCommand2 : public Command
{
string arg;
public:
ConcreteCommand2(const string & a) : arg(a) {}
void execute() override
{
cout<< "#2 process..."<<arg<<endl;
}
};
class MacroCommand : public Command
{
vector<Command*> commands;
public:
void addCommand(Command *c) { commands.push_back(c); }
void execute() override
{
for (auto &c : commands)
{
c->execute();
}
}
};
int main()
{
ConcreteCommand1 command1(receiver, "Arg ###");
ConcreteCommand2 command2(receiver, "Arg $$$");
MacroCommand macro;
macro.addCommand(&command1);
macro.addCommand(&command2);
macro.execute();
}
對比
- 繼承了抽象基類
Command
的派生類,表現出來的就是一種具體的行爲。甚至可以定義一些宏命令MacroCommand
要點總結
- Command模式的根本目的在於將“行爲請求者”和“行爲實現者”解耦,在面嚮對象語言中,常見的實現手段是“將行爲抽象爲對象”
- 實現Command接口的具體命令對象ConcreteCommand有時候根據需要可能會保存一些額外的狀態信息。通過使用Composite模式,可以將多個“命令”封裝成一個“複合命令”MacroCommand
- Command模式與C++中的函數對象有些類似。但兩者定義行爲接口的規範有所區別:Command以面向對象中的“接口-實現”來定義行爲接口規範,更嚴格,但有性能損失;C++函數對象以函數簽名來定義行爲接口規範,更靈活,性能更高