一、模式动机
在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计,使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活。
命令模式可以对发送者和接收者完全解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。这就是命令模式的模式动机。
二、模式定义
命令模式(Command Pattern):将 “请求”封装成对象,以便使用不同的请求、队列或日志来参数化其他对象。命令模式也支持撤销的操作。
命令模式包含四个角色:抽象命令类中声明了用于执行请求的execute()等方法,通过这些方法可以调用请求接收者的相关操作;具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,将接收者对象的动作绑定其中;调用者即请求的发送者,又称为请求者,它通过命令对象来执行请求;接收者执行与请求相关的操作,它具体实现对请求的业务处理。UML类图:
三、模式示例
请为巴斯特家电自动化公司设计一个家电自动化遥控器的API。
C++代码实现
#include <iostream>
#include "windows.h"
using namespace std;
class Command
{
public:
virtual void execute() = 0;
virtual void undo() {}
Command()
{
}
~Command()
{
}
};
class Light
{
public:
void on()
{
cout << "light on" << endl;
}
void off()
{
cout << "light off" << endl;
}
Light();
~Light();
};
Light::Light()
{
}
Light::~Light()
{
}
class NoCommand : public Command
{
public:
void execute()
{
cout << "nothing" << endl;
}
void undo()
{
cout << "nothing" << endl;
}
NoCommand();
~NoCommand();
};
NoCommand::NoCommand()
{
}
NoCommand::~NoCommand()
{
}
class LightOnCommand : public Command
{
public:
void execute()
{
light_->on();
}
void undo()
{
light_->off();
}
LightOnCommand(Light* light);
~LightOnCommand();
private:
Light* light_;
};
LightOnCommand::LightOnCommand(Light* light)
:light_(light)
{
}
LightOnCommand::~LightOnCommand()
{
}
class LightOffCommand : public Command
{
public:
void execute()
{
light_->off();
}
void undo()
{
light_->on();
}
LightOffCommand(Light* light);
~LightOffCommand();
private:
Light* light_;
};
LightOffCommand::LightOffCommand(Light* light)
:light_(light)
{
}
LightOffCommand::~LightOffCommand()
{
}
class RemoteControl
{
public:
void setCommand(int slot, Command* on_command, Command* off_command)
{
on_command_[slot] = on_command;
off_command_[slot] = off_command;
}
void onButtonWasPushed(int slot)
{
on_command_[slot]->execute();
}
void offButtonWasPushed(int slot)
{
off_command_[slot]->execute();
}
RemoteControl();
~RemoteControl();
private:
Command* on_command_[7];
Command* off_command_[7];
};
RemoteControl::RemoteControl()
{
Command* no_command = new NoCommand;
for (int i = 0; i < 7; i++)
{
on_command_[i] = no_command;
off_command_[i] = no_command;
}
}
RemoteControl::~RemoteControl()
{
}
int _tmain(int argc, _TCHAR* argv[])
{
RemoteControl remote_control;
Light* light = new Light;
LightOnCommand* living_room_light_on = new LightOnCommand(light);
LightOffCommand* living_room_light_off = new LightOffCommand(light);
remote_control.setCommand(0, living_room_light_on, living_room_light_off);
remote_control.onButtonWasPushed(0);
remote_control.offButtonWasPushed(0);
remote_control.onButtonWasPushed(1);
system("pause");
return 0;
}
运行结果
四、分析总结
命令模式将发出请求的对象和执行请求的对象解耦。在被解耦的两个对象之间是通过命令对象进行沟通的。命令对象封装了接收者和一个或一组动作。
优点:
- 降低系统的耦合度。
- 新的命令可以很容易地加入到系统中。
- 可以比较容易地设计一个命令队列和宏命令(组合命令)。
- 可以方便地实现对请求的Undo和Redo。
缺点:
- 使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。