命令模式(Command Pattern)
定义
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作
- Command类,用来声明执行操作的接口
- ConcreteCommand,将一个接收者对象绑定于一个操作,调用接收者相应的操作,以实现Execute
- Invoker类,要求该命令执行这个请求
- Receiver类,知道如何实施与执行一个与请求相关的操作,任何类都可能作为一个接收者。
常用场景
对于大多数请求-响应模式的功能,比较适合使用命令模式,正如命令模式定义说的那样,命令模式对实现记录日志、撤销操作等功能比较方便。
优缺点
优点:
- 它能较容易地设计一个命令队列;
- 在需要的情况下,可以较容易地将命令记入日志;
- 允许接收请求的一方决定是否要否决请求。
- 可以容易地实现对请求的撤销和重做;
- 由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易。
缺点:
命令如果很多,开发起来就要头疼了。特别是很多简单的命令,实现起来就几行代码的事,而使用命令模式的话,不用管命令多简单,都需要写一个命令类来封装
C++实现
举例:几何操作
在开发一个几何系统时,我们经常需要对一个几何对象进行平移、旋转、撤销、恢复等操作,这时候就可以采用命令模式,只对命令中心增加或删除命令,而不需关心每个命令的执行过程。
类图:
代码:
/*!
*@file Command.h
*@brief 命令模式
*/
#ifndef COMMAND_H
#define COMMAND_H
#include <iostream>
#include <vector>
#include <algorithm>
// 几何类
class Geometry
{
public:
Geometry() {}
~Geometry() {}
void Rotate()
{
std::cout << "Rotate" << std::endl;
}
void Translate()
{
std::cout << "Translate" << std::endl;
}
void Scale()
{
std::cout << "Scale" << std::endl;
}
};
// 命令基类
class Command
{
public:
Command(Geometry* geo) : m_geo(geo) {}
virtual ~Command() {}
virtual void Excute() = 0;
protected:
Geometry* m_geo;
};
// 旋转命令
class RotateCommand : public Command
{
public:
RotateCommand(Geometry* geo) : Command(geo) {}
~RotateCommand() {}
void Excute()
{
m_geo->Rotate();
}
};
// 平移命令
class TranslateCommand : public Command
{
public:
TranslateCommand(Geometry* geo) : Command(geo) {}
~TranslateCommand() {}
void Excute()
{
m_geo->Translate();
}
};
// 缩放命令
class ScaleCommand : public Command
{
public:
ScaleCommand(Geometry* geo) : Command(geo) {}
~ScaleCommand() {}
void Excute()
{
m_geo->Scale();
}
};
// 命令中心
class CommandManager
{
public:
CommandManager() {}
~CommandManager()
{
for (auto iter = m_commands.cbegin(); iter != m_commands.cend(); ++iter)
{
delete *iter;
}
m_commands.clear();
}
void AddCommand(Command* command)
{
m_commands.push_back(command);
}
void RemoveCommand(Command* command)
{
auto it = find(m_commands.begin(), m_commands.end(), command);
if (it != m_commands.end())
{
delete *it;
m_commands.erase(it);
}
}
void Excute()
{
for (auto it = m_commands.cbegin(); it != m_commands.cend(); it++)
{
(*it)->Excute();
}
}
private:
std::vector<Command*> m_commands;
};
void CommandTest()
{
Geometry* pGeo = new Geometry;
RotateCommand* pRotateCommand = new RotateCommand(pGeo);
TranslateCommand* pTranslateCommand = new TranslateCommand(pGeo);
ScaleCommand* pScaleCommand = new ScaleCommand(pGeo);
CommandManager* pManager = new CommandManager;
pManager->AddCommand(pRotateCommand);
pManager->AddCommand(pScaleCommand);
pManager->AddCommand(pTranslateCommand);
pManager->RemoveCommand(pScaleCommand);
pManager->Excute();
delete pManager;
delete pGeo;
pManager = nullptr;
pGeo = nullptr;
}
#endif // COMMAND_H