定义:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能
Command类:是一个抽象类,类中对需要执行的命令进行声明,一般来说要对外公布一个execute方法用来执行命令和undo撤销操作
ConcreteCommand类:Command类的实现类,对抽象类中声明的方法进行实现
Client类:最终的客户端调用类
以上三个类的作用应该是比较好理解的,下面我们重点说一下Invoker类和Recevier类。
Invoker类:命令的指挥者,负责设置命令和发起命令
Receiver类:执行命令具体操作的对象
以下以一个游戏例子来展示
class Actor {
private:
int x, y;
public:
Actor() { x = 0; y = 0; }
int getX() { return x; }
int getY() { return y; }
void moveTo(int x, int y) { this->x = x; this->y = y; cout << x << "," << y << endl; }
};
class Command{
public:
virtual ~Command(){}
virtual void execute() = 0;
virtual void undo() = 0;
};
class MoveCommand : public Command{
class MoveCommand : public Command {
public:
MoveCommand(Actor *actor, int x, int y) {
m_x = x;
m_y = y;
m_actor = actor;
}
void execute() {
m_beforeX = m_actor->getX();
m_beforeY = m_actor->getY();
m_actor->moveTo(m_x, m_y);
}
void undo() {
m_actor->moveTo(m_beforeX, m_beforeY);
}
private:
int m_x, m_y;
int m_beforeX, m_beforeY;
Actor *m_actor;
};
MoveCommand 是ConcreteCommand,实现了Commad接口的操作
Actor是Receiver,因为actor是实际操作的执行者
class Invoker {
public:
Invoker() {
cmdList = new stack<Command*>();
}
void RunCommad(Command *cmd) {
cmdList->push(cmd);
cmd->execute();
}
void UndoCommad() {
cmdList->top()->undo();
cmdList->pop();
}
private:
stack<Command*> *cmdList;
};
class Client {
public:
void Play() {
Actor actor;
Invoker invoker;
Command *c1 = new MoveCommand(&actor, 1, 1);
invoker.RunCommad(c1);
Command *c2 = new MoveCommand(&actor, 2, 2);
invoker.RunCommad(c2);
invoker.UndoCommad();
}
};
命令模式的适用场景:
系统需要支持命令的撤销(undo),命令对象还可以提供redo方法,以供客户端在需要时,再重新实现命令效果
系统需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命周期。这时命令的接受者可以在本地,也可以在网络的另一个地址。命令对象可以串行地传送到接受者上去。
如果一个系统要将系统中所有的数据消息更新到日志里,以便在系统崩溃时,可以根据日志里读回所有数据的更新命令,重新调用方法来一条一条地执行这些命令,从而恢复系统在崩溃前所做的数据更新
系统需要使用命令模式作为“CallBack(回调)”在面向对象系统中的替代。Callback即是先将一个方法注册上,然后再以后调用该方法
命令模式的优缺点
命令模式使得命令发出的一个和接收的一方实现低耦合,从而有以下的优点:
- 命令模式使得新的命令很容易被加入到系统里
- 可以设计一个命令队列来实现对请求的Undo和Redo操作
- 可以较容易地将命令写入日志
- 可以把命令对象聚合在一起,合成为合成命令。合成命令式合成模式的应用
命令模式的缺点:
- 使用命令模式可能会导致系统有过多的具体命令类。这会使得命令模式在这样的系统里变得不实际
命令模式总结:
它能很容易的维护所有命令的集合(该命令集合的命令实际调用许多参数不一样的函数,例如
moveTo(Actor *actor, int x, int y)
和fire(Actor *self, Actor target)
)它可以很方便的实现撤销和恢复命令
可以很方便的将每个执行记录日志
最重要的就是将发起者与实现者分离(一套Actor操作的实现可以由AI和人两种发起者来发起)