命令模式封装请求以解耦发送者与接收者,策略模式封装算法以实现运行时替换。命令模式的核心在于将请求封装为对象,使能支持撤销、日志、排队等功能,主要涉及command、concretecommand、receiver、invoker和client五个角色;而策略模式通过封装不同的算法族,允许算法独立变化并可在运行时切换,核心在于算法的可替换性。两者虽均基于接口设计思想,但应用场景不同:命令模式适用于gui操作、事务处理、游戏动作记录等需保存或撤销请求的场景,策略模式则适用于支付方式、排序算法等需动态切换行为的场景。

C++命令模式的核心在于将请求封装成一个对象,从而允许你参数化客户端对象,支持请求排队、记录请求日志,以及支持可撤销的操作。简单来说,就是把“做什么”和“谁去做”解耦。

解决方案

命令模式涉及几个关键角色:
立即学习“C++免费学习笔记(深入)”;

- Command(命令接口): 声明执行操作的接口。
- ConcreteCommand(具体命令): 将一个接收者对象绑定于一个动作。调用接收者相应的操作,以实现 Execute。
- Receiver(接收者): 知道如何实施与执行一个请求相关的操作。任何类都可以作为接收者。
- Invoker(调用者): 要求命令执行请求。
- Client(客户端): 创建 ConcreteCommand 对象并设置其接收者。
以下是一个简单的C++命令模式示例:
#include#include // Receiver class Light { public: void turnOn() { std::cout << "Light is ON" << std::endl; } void turnOff() { std::cout << "Light is OFF" << std::endl; } }; // Command Interface class Command { public: virtual void execute() = 0; virtual ~Command() {} }; // Concrete Command class TurnOnCommand : public Command { private: Light* light; public: TurnOnCommand(Light* light) : light(light) {} void execute() override { light->turnOn(); } }; // Concrete Command class TurnOffCommand : public Command { private: Light* light; public: TurnOffCommand(Light* light) : light(light) {} void execute() override { light->turnOff(); } }; // Invoker class RemoteControl { private: std::vector commands; public: void setCommand(Command* command) { commands.push_back(command); } void pressButton() { if (!commands.empty()) { commands.back()->execute(); commands.pop_back(); // 执行后移除,简化示例 } else { std::cout << "No command set" << std::endl; } } }; int main() { Light* livingRoomLight = new Light(); TurnOnCommand* turnOnCmd = new TurnOnCommand(livingRoomLight); TurnOffCommand* turnOffCmd = new TurnOffCommand(livingRoomLight); RemoteControl* remote = new RemoteControl(); remote->setCommand(turnOnCmd); remote->setCommand(turnOffCmd); remote->pressButton(); // Light is OFF (因为后设置的是TurnOffCommand) remote->pressButton(); // Light is ON delete turnOnCmd; delete turnOffCmd; delete livingRoomLight; delete remote; return 0; }
这个例子中,Light是接收者,TurnOnCommand和TurnOffCommand是具体命令,RemoteControl是调用者。 客户端负责创建命令并将它们传递给调用者。
命令模式在C++中有什么实际应用场景?
命令模式在GUI应用程序、事务处理、宏记录、以及游戏开发中都有广泛应用。例如,在文本编辑器中,每个操作(如复制、粘贴、删除)都可以封装成一个命令对象。这样做的好处是,可以很容易地实现撤销/重做功能,只需要维护一个命令历史记录即可。此外,命令模式还可以用于实现复杂的事务处理,确保一系列操作要么全部成功执行,要么全部回滚。 在游戏开发中,玩家的每一个操作(如移动、攻击)都可以视为一个命令,便于记录和回放。
C++中如何实现命令的撤销(Undo)和重做(Redo)?
实现撤销和重做功能,需要在Command接口中添加undo()方法,并在ConcreteCommand中实现该方法。同时,需要维护一个命令历史栈,用于记录已执行的命令。
// Command Interface (with Undo)
class Command {
public:
virtual void execute() = 0;
virtual void undo() = 0; // 添加 undo 方法
virtual ~Command() {}
};
// Concrete Command (with Undo)
class TurnOnCommand : public Command {
private:
Light* light;
bool previousState; // 记录之前的状态,以便 undo
public:
TurnOnCommand(Light* light) : light(light), previousState(false) {}
void execute() override {
previousState = (/*假设Light有个getState()方法*/ false); // 实际应用中需要获取灯的当前状态
light->turnOn();
}
void undo() override {
if (previousState) {
light->turnOn(); // 恢复到之前的状态(如果之前是开着的)
} else {
light->turnOff(); // 恢复到之前的状态(如果之前是关着的)
}
}
};
//Invoker (with Undo/Redo)
class RemoteControl {
private:
std::vector commandHistory;
int currentCommandIndex = -1; // 指向当前执行的命令
public:
void executeCommand(Command* command) {
command->execute();
if (currentCommandIndex < (int)commandHistory.size() - 1) {
// 如果执行了undo之后的新命令,则清除后面的历史
commandHistory.erase(commandHistory.begin() + currentCommandIndex + 1, commandHistory.end());
}
commandHistory.push_back(command);
currentCommandIndex++;
}
void undo() {
if (currentCommandIndex >= 0) {
commandHistory[currentCommandIndex]->undo();
currentCommandIndex--;
}
}
void redo() {
if (currentCommandIndex < (int)commandHistory.size() - 1) {
currentCommandIndex++;
commandHistory[currentCommandIndex]->execute();
}
}
};
int main() {
Light* livingRoomLight = new Light();
RemoteControl* remote = new RemoteControl();
TurnOnCommand* turnOnCmd = new TurnOnCommand(livingRoomLight);
TurnOffCommand* turnOffCmd = new TurnOffCommand(livingRoomLight);
remote->executeCommand(turnOnCmd); // Light is ON
remote->executeCommand(turnOffCmd); // Light is OFF
remote->undo(); // Light is ON
remote->undo(); // Light is OFF
remote->redo(); // Light is ON
remote->redo(); // Light is OFF
delete turnOnCmd;
delete turnOffCmd;
delete livingRoomLight;
delete remote;
return 0;
} 注意,undo()的实现需要根据具体情况进行调整,通常需要记录命令执行前的状态。此外,撤销/重做功能的实现还需要考虑内存管理,避免内存泄漏。
命令模式和策略模式有什么区别?
虽然命令模式和策略模式都涉及到封装,但它们的目的不同。命令模式封装的是请求,而策略模式封装的是算法。 命令模式的重点在于解耦请求的发送者和接收者,允许请求排队、记录日志等。 策略模式的重点在于提供一种在运行时选择算法的方式,使得算法可以独立于使用它的客户端而变化。 策略模式关注的是算法的可替换性,而命令模式关注的是请求的封装和执行。 简单来说,策略模式解决的是“用哪个算法”,命令模式解决的是“做什么操作”。










