一、定义
命令模式是从界面设计中提取出来的一种分离耦合,提高重用的方法。被认为是最优雅而且简单的模式,它的应用范围非常广泛。
《设计模式》中命令模式的定义为:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。
在我看来,其实命令模式像很多设计模式一样——通过在你的请求和处理之间加上了一个中间人的角色,来达到分离耦合的目的。通过对中间人角色的特殊设计来形成不同的模式。当然命令模式就是一种特殊设计的结果。
二、结构
看下命令模式是有哪些角色来组成的吧。
1) 命令角色( Command):声明执行操作的接口。
2) 具体命令角色( ConcreteCommand):将一个接收者对象绑定于一个动作;调用接收者相应的操作,以实现命令角色声明的执行操作的接口。
3) 客户角色( Client):创建一个具体命令对象(并可以设定它的接收者)。
4) 请求者角色(Invoker):调用命令对象执行这个请求。
5) 接收者角色(Receiver):知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者。
三、实现
#include <iostream>
using namespace std;
#define SAFE_DELETE(p) if (p) { delete p; p = NULL; }
//请求者角色( Invoker):调用命令对象执行这个请求。
class Receiver
{
public:
void Action()
{
cout<<"Receiver->Action"<<endl;
}
};
//命令角色( Command):声明执行操作的接口。
class Command
{
public:
virtual void Execute() = 0;
};
//具体命令角色( Concrete Command):将一个接收者对象绑定于一个动作;
//调用接收者相应的操作,以实现命令角色声明的执行操作的接口。
class ConcreteCommand : public Command
{
public:
ConcreteCommand(Receiver *pReceiver) : m_pReceiver(pReceiver){}
void Execute()
{
m_pReceiver->Action();
}
private:
Receiver *m_pReceiver;
};
//请求者角色( Invoker):调用命令对象执行这个请求。
class Invoker
{
public:
Invoker(Command *pCommand) : m_pCommand(pCommand){}
void Invoke()
{
m_pCommand->Execute();
}
private:
Command *m_pCommand;
};
int main()
{
Receiver *pReceiver = new Receiver();
Command *pCommand = new ConcreteCommand(pReceiver);
Invoker *pInvoker = new Invoker(pCommand);
pInvoker->Invoke();
SAFE_DELETE(pInvoker);
SAFE_DELETE(pCommand);
SAFE_DELETE(pReceiver);
return 0;
}
命令模式还有一个常见的用法就是执行事务操作。这就是为什么命令模式还叫做事务模式的原因吧。它可以在请求被传递到接收者角色之前,检验请求的正确性,甚至可以检查和数据库中数据的一致性,而且可以结合组合模式的结构,来一次执行多个命令。
使用命令模式不仅仅可以解除请求者和接收者之间的耦合,而且可以用来做批处理操作,这完全可以发挥你自己的想象——请求者发出的请求到达命令角色这里以后,先保存在一个列表中而不执行;等到一定的业务需要时,命令模式再将列表中全部的操作逐一执行。
四、优点及适用情况
1) 命令模式将调用操作的请求对象与知道如何实现该操作的接收对象解耦。
2)具体命令角色可以被不同的请求者角色重用。
3) 你可将多个命令装配成一个复合命令。
4) 增加新的具体命令角色很容易,因为这无需改变已有的类。
GOF 总结了命令模式的以下适用环境。
1) 需要抽象出待执行的动作,然后以参数的形式提供出来——类似于过程设计中的回调机制。而命令模式正是回调机制的一个面向对象的替代品。
2) 在不同的时刻指定、排列和执行请求。一个命令对象可以有与初始请求无关的生存期。
3) 需要支持取消操作。
4) 支持修改日志功能。这样当系统崩溃时,这些修改可以被重做一遍。
5) 需要支持事务操作。