C++设计模式——Command命令模式
创作时间:
作者:
@小白创作中心
C++设计模式——Command命令模式
引用
1
来源
1.
https://cloud.tencent.com/developer/article/2433299
命令模式是一种常用的设计模式,它将一个请求的处理或者一个具体操作封装为一个对象,从而可以让开发者根据不同的请求参数来生成不同的执行函数。本文将详细介绍命令模式的定义、结构、应用场景以及优缺点,并通过多个代码示例帮助读者更好地理解这一设计模式。
一、命令模式的定义
命令模式是一种行为型设计模式。在实际开发场景中,命令模式将一个请求的处理或者一个具体操作封装为一个对象,从而可以让开发者根据不同的请求参数来生成不同的执行函数。
命令模式的本质是对具体命令的拆解和封装,实现命令发送者和命令接收者的解耦。命令模式使得具体的命令可以被存储和传递,由命令接收者来指定这个命令何时被执行、撤销等。命令模式中的发送者只需要关注命令的发送即可,不需要关注具体命令的执行流程。
命令模式在现实生活中的抽象实例:
- 遥控器:通过在遥控器上按下不同的按钮来执行不同的操作,遥控器使得发送者(用户)与接收者(比如电视、空调等)解耦。
- 餐厅点餐:在点餐过程中,将顾客的点餐请求封装成命令对象并发送给厨师,厨师作为命令接收者根据不同的命令进行菜肴的烹饪。
- 编辑器的撤销功能:编辑器将用户的操作命令全部保存在一个命令堆栈中,让用户可以随时撤销执行的命令,从而实现了对操作的灵活控制。
- 订单系统:用户下单时,订单系统会将用户的选购操作封装成一条命令(即生成订单)并发送给库存管理系统来处理,将用户与仓库发货解耦。
二、命令模式的结构
命令模式主要包含以下组件:
- 抽象命令接口(Command):
- 定义了命令的执行方法,内部包含一个execute()函数,用于定义命令的请求过程。
- 具体命令(ConcreteCommand):
- 是抽象命令接口的具体实现,包含具体命令的执行细节,同时内部可能还包含指向接收者的指针,与接收者相互关联。
- 请求者(Invoker):
- 也叫触发者,负责维护命令列表(addCommand),并调用命令对象的execute()接口。请求者不需要知道具体命令的实际操作,只关注如何将命令发送给命令对象。
- 接收者(Receiver):
- 接收者内部包含了去执行命令的实际操作的对象。接收者只关注命令的实际操作细节,并被具体命令对象(ConcreteCommand)所调用。
- 客户端(Client):
- 负责创建具体命令并将命令发送给请求者对象。
组件之间的工作步骤如下:
- 客户端创建具体命令对象,并指定与命令对象关联的接收者。
- 将具体命令对象传递给请求者对象。
- 请求者对象接收到具体命令对象后,将其存储到命令列表中。
- 请求者对象执行具体命令对象的execute()方法。
- 具体命令对象将命令传递给接收者对象。
- 接收者对象执行实际操作。
三、命令模式代码样例
Demo1:不包含Receiver
#include <iostream>
#include <string>
#include <vector>
class Command {
public:
virtual void execute() = 0;
};
class ConcreteCommand : public Command {
private:
std::string receiver_;
public:
ConcreteCommand(const std::string& receiver) {
receiver_ = receiver;
}
void execute() override {
std::cout << "ConcreteCommand: " << receiver_ << "\n";
}
};
class Invoker {
private:
std::vector<Command*> commands_;
public:
void addCommand(Command* command) {
commands_.push_back(command);
}
void executeCommands() {
for (auto command : commands_) {
command->execute();
}
commands_.clear();
}
};
int main() {
Invoker invoker;
Command* command1 = new ConcreteCommand("command 01 -> ");
Command* command2 = new ConcreteCommand("command 02 -> ");
invoker.addCommand(command1);
invoker.addCommand(command2);
invoker.executeCommands();
delete command1;
delete command2;
return 0;
}
运行结果:
ConcreteCommand: command 01 ->
ConcreteCommand: command 02 ->
Demo2:包含Receiver
#include <iostream>
#include <vector>
class Command {
public:
virtual void execute() = 0;
};
class Receiver {
public:
Receiver(std::string cmd_str)
{
cmd = cmd_str;
}
void action() {
std::cout << "Operating " << cmd << std::endl;
}
private:
std::string cmd;
};
class ConcreteCommand : public Command {
private:
Receiver* receiver;
public:
ConcreteCommand(Receiver* receiver){
this->receiver = receiver;
}
void execute() override {
receiver->action();
}
};
class Invoker {
private:
std::vector<Command*> commands;
public:
void addCommand(Command* command) {
commands.push_back(command);
}
void executeCommands() {
for (auto command : commands) {
command->execute();
}
commands.clear();
}
};
int main() {
Receiver* receiver1 = new Receiver("action_01");
Receiver* receiver2 = new Receiver("action_02");
Command* command1 = new ConcreteCommand(receiver1);
Command* command2 = new ConcreteCommand(receiver2);
Invoker invoker;
invoker.addCommand(command1);
invoker.addCommand(command2);
invoker.executeCommands();
delete command1;
delete command2;
delete receiver1;
delete receiver2;
return 0;
}
运行结果:
Operating action_01
Operating action_02
四、命令模式的应用场景
- 撤销或重做功能实现:在编辑器或应用程序中,用户可以执行“撤销”或“重做”操作,这些操作可以被组织成命令链,方便管理。
- 事件驱动软件开发:将不同事件封装为命令对象,当某一事件发生时执行相应的命令处理逻辑。
- 远程通信软件开发:将通信过程封装成发送者和接收者解耦的结构,隐藏通信的具体细节。
五、命令模式的优缺点
命令模式的优点:
- 命令模式将发送者和接收者解耦,使得两者可以分别独立变化。
- 扩展性好,新的命令可以很容易地添加和维护,不影响现有系统。
- 使用对象来存储命令,很适用于开发回滚和撤销操作。
- 可以使用队列将命令进行缓存,实现延迟执行或者异步处理。
命令模式的缺点:
- 增加了一些额外的抽象层次,使代码结构变得复杂。
- 命令的具体操作包含了对象的动态创建和销毁,性能开销大。
- 对象之间存在着多层次的依赖,维护变得困难,不易于bug定位和调试。
六、代码实战
基于命令模式实现的模拟远程灯光控制
#include <iostream>
using namespace std;
class Command
{
public:
virtual void execute() = 0;
};
//Receiver
class Light
{
public:
void on() {
cout << "The light is on\n";
}
void off() {
cout << "The light is off\n";
}
};
class LightOnCmd: public Command
{
public:
LightOnCmd(Light* light){
mLight = light;
}
void execute() {
mLight->on();
}
private:
Light* mLight;
};
class LightOffCmd: public Command
{
public:
LightOffCmd(Light* light){
mLight = light;
}
void execute() {
mLight->off();
}
private:
Light* mLight;
};
//Invoker
class RemoteControl
{
public:
void setCommand(Command* cmd) {
mCmd = cmd;
}
void buttonPressed() {
mCmd->execute();
}
private:
Command* mCmd;
};
int main()
{
Light* light = new Light;
LightOnCmd* lightOn = new LightOnCmd(light);
LightOffCmd* lightOff = new LightOffCmd(light);
RemoteControl* control = new RemoteControl;
control->setCommand(lightOn);
control->buttonPressed();
control->setCommand(lightOff);
control->buttonPressed();
delete light;
delete lightOn;
delete lightOff;
delete control;
return 0;
}
运行结果:
The light is on
The light is off
七、参考阅读
热门推荐
宝宝拉绿便?饮食调整有妙招
临沧人真的把“鸡”吃明白了
没胃口食不下的时候,就要来一道手撕鸡
母乳vs配方奶:宝宝绿便的真相揭秘
母乳喂养宝宝为何拉绿便?
王星越蓝盈莹爆红!文荣奖颁奖典礼亮点揭秘
陈凯歌点赞横店速度,文荣奖见证影视新人崛起
朱亚文揭秘文荣奖评选标准:真心与真实成关键
横店影视城:从“东方好莱坞”到影视人的梦想乐园
弹床运动的好处:全面提升身心健康的趣味运动
康复床上运动:定义、方法与效果评价
欧联杯曼彻斯特联vs格拉斯哥流浪者前瞻分析 阿莫林将在上周的失利后寻求反弹
KPL为何刺客只有娜可露露,娜可露露和兰陵王哪个厉害
横店影视文荣奖最新评选标准揭秘:文化自信成评审关键词
横店影视文荣奖:青年演员的高光时刻
鲁菜健康吃法,你get了吗?
麻腮风疫苗常见问答
横店影视文荣奖:盘点那些爆火的影视剧
横店文荣奖揭晓:彭昱畅张榕容摘得最佳青年演员桂冠
横店影视文荣奖再现历史风云
桑黄:森林中的金色瑰宝
桑黄:天然的健康守护神
揭秘“南方人参”桑黄的神奇药效
陈凯歌、朱亚文亮相横店文荣奖:见证“横店速度”,谁是你心中的最佳?
王星越蓝盈莹再创佳绩,第十届横店文荣奖揭晓
日语学习:超实用「谢谢的日语」这样说更到位,别再只会「ありがとう」!
赵露思《珠帘玉幕》演技再遭质疑:从新人到实力派的转型之痛
赵露思新剧《恋人》引热议:从古偶女神到实力派的蜕变之路
低盐饮食真的能降血压吗?最新研究给出答案
中医养生:非药物降压新潮流