命令模式是我们能够实现发送者和接收者之间的完全解耦,发送者是调用操作的对象,而接收者是接收请求并执行特定操作的对象。通过解耦,发送者无需了解接收者的接口。在这里,请求的含义是需要被执行的命令。
作用
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。当将客户的单个请求封装成对象以后,我们就可以对这个请求存储更多的信息,使请求拥有更多的能力;命令模式能够把请求发送者和接收者解耦,使得命令发送者不用去关心请求将以何种方式被处理。
类视图
实现
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操作重新执行这些命令。
- 通过在原生操作基础上的高层操作构建系统。这样的结构在支持交易操作的系统中很常见。一个交易事物封装一组变化的数据,命令模式提供了一种交易模型,命令都有一个共同的接口,允许你使用相同的方式调用所有的交易。这种模式也使得它很容易与新的交易系统进行交互扩展。