命令模式详解(附带C++实例)
封装是面向对象编程的基石之一,它不仅保护了数据的安全性,也使得代码更易于管理和扩展。在设计模式中,封装行为的思想被运用于多种模式中,其中就包括命令模式。
命令模式将请求或简单操作封装成对象,允许用户以参数的形式在程序中存储、传递和返回消息或请求调用。这种模式的核心在于分离了发送命令的对象和接收并执行命令的对象。
命令模式的应用场景和优势如下:
其核心设计准则包括:
命令模式的活动图如下图所示,展示了这些角色如何交互。

图 1 命令模式
命令模式将请求或简单操作封装成对象,允许用户以参数的形式在程序中存储、传递和返回消息或请求调用。这种模式的核心在于分离了发送命令的对象和接收并执行命令的对象。
命令模式的应用场景和优势如下:
- 事务行为:例如,撤销和重做功能在文本编辑器或图形编辑工具中极为常见,命令模式允许将执行操作的历史记录下来,便于未来进行回溯。
- 任务调度:在需要安排任务、排队执行或者通过网络发送请求的应用中,命令对象可以作为数据传递。例如,在一个智能家居控制系统中,用户的单击动作可以封装成命令对象,无论是开灯、调节温度还是关闭安全系统,每个命令都可以被日志记录、调度或者撤销。
命令模式的设计准则
命令模式主要是将操作封装为对象,允许将函数调用、请求或某些操作延迟到其他对象中进行处理。其核心设计准则包括:
- 接口隔离:命令模式要求将调用操作和执行操作的对象隔离开来。这通常通过定义一个统一的命令接口实现,该接口具备一个执行方法,所有的命令对象都必须实现这个接口;
- 可扩展性:设计应允许轻松添加新命令而不影响现有代码。每个命令都独立为一个类,增加新命令只需新增类而无须修改现有结构;
- 撤销操作:命令模式通常配合撤销功能实现,这要求命令对象存储足够的状态信息,以便于恢复到执行命令前的状态;
- 命令聚合与宏命令:命令可以组合成宏命令,即一个命令的执行可以触发多个命令。这需要设计支持组合的命令对象。
命令模式中的角色
命令模式主要包括以下几个角色:- 客户端:创建具体命令对象,并设置其接收者;
- 调用者:要求命令执行请求;
- 命令接口:声明执行操作的接口;
- 具体命令:将一个接收者对象与一个动作绑定,调用接收者相应的操作;
- 接收者:知道如何实施与执行一个请求相关的操作。
命令模式的活动图如下图所示,展示了这些角色如何交互。

图 1 命令模式
C++实现命令模式
下面是一个使用命令模式的 C++ 示例代码。在这个例子中,我们将模拟一个简单的遥控器,可以控制灯的开和关。代码中将展示命令模式的各个角色。#include <iostream> #include <vector> #include <memory> // 命令接口 class Command { public: virtual ~Command() {} virtual void execute() = 0; }; // 接收者类 class Light { public: void turnOn() { std::cout << "Light is on." << std::endl; } void turnOff() { std::cout << "Light is off." << std::endl; } }; // 具体命令类:开灯 class LightOnCommand : public Command { private: Light& light; public: LightOnCommand(Light& light) : light(light) {} void execute() override { light.turnOn(); } }; // 具体命令类:关灯 class LightOffCommand : public Command { private: Light& light; public: LightOffCommand(Light& light) : light(light) {} void execute() override { light.turnOff(); } }; // 调用者类 class RemoteControl { private: std::vector<std::unique_ptr<Command>> onCommands; std::vector<std::unique_ptr<Command>> offCommands; public: void setCommand(size_t slot, std::unique_ptr<Command> onCmd, std::unique_ptr<Command> offCmd) { if (slot >= onCommands.size()) { onCommands.resize(slot + 1); offCommands.resize(slot + 1); } onCommands[slot] = std::move(onCmd); offCommands[slot] = std::move(offCmd); } void pressOnButton(size_t slot) { if (slot < onCommands.size() && onCommands[slot] != nullptr) { onCommands[slot]->execute(); } } void pressOffButton(size_t slot) { if (slot < offCommands.size() && offCommands[slot] != nullptr) { offCommands[slot]->execute(); } } }; // 客户端代码 int main() { Light livingRoomLight; RemoteControl remote; remote.setCommand(0, std::make_unique_ptr<LightOnCommand>(livingRoomLight), std::make_unique_ptr<LightOffCommand>(livingRoomLight)); remote.pressOnButton(0); // Light is on. remote.pressOffButton(0); // Light is off. return 0; }在这段代码中:
- Light 是一个接收者,实现了开灯和关灯的操作;
- LightOnCommand 和 LightOffCommand 是具体命令,实现了 Command 接口,并调用 Light 的方法来执行具体操作;
- RemoteControl 是调用者,持有命令对象,并在合适的时刻触发它们;
- 在 main 函数中,我们充当客户端,创建具体命令并将它们关联到接收者和调用者。