当前位置: 首页 > news >正文

C++设计模式——Command命令模式

一,命令模式的定义

命令模式是一种行为型设计模式。在实际开发场景中,命令模式将一个请求的处理或者一个具体操作封装为一个对象,从而可以让开发者根据不同的请求参数来生成不同的执行函数。

命令模式的本质是对具体命令的拆解和封装,实现命令发送者和命令接收者的解耦。

命令模式使得具体的命令可以被存储和传递,由命令接收者来指定这个命令何时被执行、撤销等。

命令模式中的发送者只需要关注命令的发送即可,不需要关注具体命令的执行流程。

命令模式在现实生活中的抽象实例:

遥控器:通过在遥控器上按下不同的按钮来执行不同的操作,遥控器使得发送者(用户)与接收者(比如电视、空调等)解耦。

餐厅点餐:在点餐过程中,将顾客的点餐请求封装成命令对象并发送给厨师,厨师作为命令接收者根据不同的命令进行菜肴的烹饪。

编辑器的撤销功能:编辑器将用户的操作命令全部保存在一个命令堆栈中,让用户可以随时撤销执行的命令,从而实现了对操作的灵活控制。

订单系统:用户下单时,订单系统会将用户的选购操作封装成一条命令(即生成订单)并发送给库存管理系统来处理,将用户与仓库发货解耦。

二,命令模式的结构

命令模式主要包含以下组件:

1.抽象命令接口(Command):

定义了命令的执行方法,内部包含一个execute()函数,用于定义命令的请求过程。

2.具体命令(ConcreteCommand):

是抽象命令接口的具体实现,包含具体命令的执行细节,同时内部可能还包含指向接收者的指针,与接收者相互关联。

3.请求者(Invoker):

也叫触发者,负责维护命令列表(addCommand),并调用命令对象的execute()接口。请求者不需要知道具体命令的实际操作,只关注如何将命令发送给命令对象。

4.接收者(Receiver):

接收者内部包含了去执行命令的实际操作的对象。接收者只关注命令的实际操作细节,并被具体命令对象(ConcreteCommand)所调用。

5.客户端(Client):

负责创建具体命令并将命令发送给请求者对象。

组件之间的工作步骤如下:

1.客户端创建具体命令对象,并指定与命令对象关联的接收者。

2.将具体命令对象传递给请求者对象。

3.请求者对象接收到具体命令对象后,将其存储到命令列表中。

4.请求者对象执行具体命令对象的execute()方法。

5.具体命令对象将命令传递给接收者对象。

6.接收者对象执行实际操作。

对应UML类图:

三,命令模式代码样例

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

七,参考阅读

https://www.geeksforgeeks.org/command-pattern/
https://www.geeksforgeeks.org/command-pattern-c-design-patterns/
https://www.bogotobogo.com/DesignPatterns/command.php
https://www.bogotobogo.com/DesignPatterns/command.php
https://www.codeproject.com/Articles/343676/Understanding-and-Implementing-the-Command-Pattern


http://www.mrgr.cn/news/20637.html

相关文章:

  • pico手柄和人物模型手部旋转同步,实现手柄控制手臂手部位置移动、手部旋转和手指的操作了
  • 2024 年高教社杯全国大学生数学建模竞赛B题4小问解题思路(第二版)
  • python circular import python循环导入问题
  • 进销存管理系统源码
  • C++ 模板进阶知识——万能引用
  • blast的快速安装使用-简易版
  • 基于STM32的RTOS--freertos的使用(HAL实现多任务)
  • 逻辑回归模型
  • vue3 通过 绑定 ref 重置 DOM
  • 【计算机网络】TCP连接如何确保传输的可靠性
  • PMP错题总结(十七)
  • Circuitjs 在线电路模拟器使用指南
  • linux curl命令介绍以及使用
  • 科研绘图系列:R语言折线图(linechart plots)
  • 2024年高教杯国赛(E题)数学建模竞赛解题思路|完整代码论文集合
  • Mybatis 多表联查
  • LCR 017
  • 9.5javaweb项目总结
  • kubelet 探针
  • 树莓派点灯(TODO)