把方法调用封装起来,调用此方法的对象不需要关心方法是怎么运行的,还可以重复使用这些封装来实现撤销(undo)。
案例:设计一个遥控器来控制不同类型的装置,比如热水器,空调,每个装置的具体步骤方法不一样。命令模式可以将“动作的请求者”从“动作的执行者”对象中解耦。在本案例中,请求者可以是遥控器,执行者就是各种装置其中之一。
如果遥控器上的每个按钮都存储一个命令对象,当按钮被按下的时候,就可以请命令对象做相关的工作。遥控器不需要知道工作内容是什么,只要相应的命令对象能和正确的对象沟通 ,把事情做好了就行。这样,遥控器就和具体的各个装置对象之间解耦了。
命令模式:把“请求”封装成对象,以便使用不同的请求。命令模式也支持可撤销的操作。
类图:
实现如下:
command.h
#ifndef COMMAND_H
#define COMMAND_H
#include <iostream>
#include <string>
using namespace std;
class Command {
public:
virtual void execute() = 0;
virtual void undo() = 0;
};
#endif // COMMAND_H
lightOnCommand.h
#ifndef LIGHTONCOMMAND_H
#define LIGHTONCOMMAND_H
#include "command.h"
#include "light.h"
class LightOnCommand : public Command {
public:
virtual void execute() override;
virtual void undo() override;
private:
Light* m_light = new Light();
};
void LightOnCommand::execute()
{
m_light->on();
}
void LightOnCommand::undo()
{
m_light->off();
}
#endif // LIGHTONCOMMAND_H
lightOffCommand.h
#ifndef LIGHTOFFCOMMAND_H
#define LIGHTOFFCOMMAND_H
#include "command.h"
#include "light.h"
class LightOffCommand : public Command {
public:
virtual void execute() override;
virtual void undo() override;
private:
Light* m_light = new Light();
};
void LightOffCommand::execute()
{
m_light->off();
}
void LightOffCommand::undo()
{
m_light->on();
}
#endif // LIGHTOFFCOMMAND_H
noCommand.h
#ifndef NOCOMMAND_H
#define NOCOMMAND_H
// 空实现
class NoCommand : public Command {
public:
virtual void execute() override;
virtual void undo() override;
};
void NoCommand::execute()
{
cout << "NoCommand" << endl;
}
void NoCommand::undo()
{
cout << "NoCommand" << endl;
}
#endif // NOCOMMAND_H
remoteControl.h
#ifndef REMOTECONTROL_H
#define REMOTECONTROL_H
#include "command.h"
#include "noCommand.h"
#include <vector>
class RemoteControl {
public:
void setCommand(int slot, Command *onCommand, Command *offCommand);
void onButtonWasPressed(int slot);
void offButtonWasPressed(int slot);
void undoButtonWasPressed();
private:
// 设置只有3个槽
// vector<Command* > m_onCommands{{(Command*)NULL}, {(Command*)NULL}, {(Command*)NULL}};
// vector<Command* > m_offCommands{{(Command*)NULL}, {(Command*)NULL}, {(Command*)NULL}};
// 使用上面的写法也可以,不过更好的写法是使用NoCommand(null object);
vector<Command* > m_onCommands{3, (Command *) new NoCommand()};
vector<Command* > m_offCommands{3, (Command *) new NoCommand()};
Command* m_undoCommand; // 保存上一次的操作
};
void RemoteControl::setCommand(int slot, Command *onCommand, Command *offCommand)
{
m_onCommands[slot] = onCommand;
m_offCommands[slot] = offCommand;
}
void RemoteControl::onButtonWasPressed(int slot)
{
m_onCommands[slot]->execute();
m_undoCommand = m_onCommands[slot];
}
void RemoteControl::offButtonWasPressed(int slot)
{
m_offCommands[slot]->execute();
m_undoCommand = m_offCommands[slot];
}
void RemoteControl::undoButtonWasPressed()
{
m_undoCommand->undo();
}
#endif // REMOTECONTROL_H
light.h
#ifndef LIGHT_H
#define LIGHT_H
#include <iostream>
using namespace std;
class Light {
public:
void on();
void off();
};
void Light::on()
{
cout << "灯打开了!" << endl;
}
void Light::off()
{
cout << "灯关闭了!" << endl;
}
#endif // LIGHT_H
main.cpp
/*
* 命令模式
*
* date:2023-9-11
*/
#include "remoteControl.h"
#include "lightOnCommand.h"
#include "lightOffCommand.h"
int main()
{
// 注意:此遥控器只有3个槽,0,1,2
RemoteControl* remoteControl = new RemoteControl();
Command* lightOnCommand = new LightOnCommand();
Command* lightOffCommand = new LightOffCommand();
cout << "购买遥控器,使用1号槽来控制灯的开关" << endl;
remoteControl->setCommand(1, lightOnCommand, lightOffCommand);
cout << "开灯" << endl;
remoteControl->onButtonWasPressed(1);
cout << "关灯" << endl;
remoteControl->offButtonWasPressed(1);
cout << "撤销刚才的操作" << endl;
remoteControl->undoButtonWasPressed();
}
运行截图:
注: 1.要想实现undo功能,就必须在遥控器类中添加一个对象来记录上一次的命令是什么。
2.如果要一个槽同时控制多个装置,可以用数组分别存储要控制打开和关闭的所有装置对象。
NoCommand对象是一个空对象(null object)的例子。当你不想返回一个有意义的对象时,空对象就很有用。客户可以把处理null的责任转移给空对象。比如说,遥控器在出厂时肯定没有设置有意义的命令对象,就可以使用NoCommand对象作为代用品,当调用它的execute()方法时,这种对象什么事情都不做。
在许多设计模式中,都会看到空对象的使用。甚至有些时候,空对象本身也被视为是一种设计模式。