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

news2024/11/15 13:26:20

一,命令模式的定义

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

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

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

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

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

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

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

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

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

二,命令模式的结构

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

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.coloradmin.cn/o/2108974.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Git远程仓库】将本地仓库推送到github(踩坑记录)

上一篇博客已经介绍了git本地仓库的基本操作&#xff0c;接下来记录一下如何将本地仓库上传到远程仓库中 远程仓库&#xff1a;托管在因特网的版本库&#xff0c;保存版本库的历史记录&#xff0c;多人协作 1. 创建远程版本库&#xff0c;得到远程仓库git地址 2. 本地仓库添加…

C#复习之内部类和分布类

知识点一&#xff1a;内部类 知识点二&#xff1a;分布类 知识点三&#xff1a;分部方法

DisplayManagerService启动-Android13

DisplayManagerService启动-Android13 1、DisplayManagerService启动1.1 简要时序图 2、DEFAULT_DISPLAY主屏幕添加3、默认屏幕亮度 1、DisplayManagerService启动 1.1 简要时序图 2、DEFAULT_DISPLAY主屏幕添加 3、默认屏幕亮度

C#复习之继承的基本规则

知识点一&#xff1a;基本概念 知识点二&#xff1a;基本语法 知识点三&#xff1a;实例 知识点四&#xff1a;访问修饰符的影响 知识点五&#xff1a;子类和父类的同名成员 总结&#xff1a;

MIT线性代数

本文链接的原创作者为 浊酒南街https://blog.csdn.net/weixin_43597208 第1讲 MIT_线性代数笔记&#xff1a;第 01 讲 行图像和列图像-CSDN博客 第2讲 MIT_线性代数笔记&#xff1a;第 02 讲 矩阵消元_矩阵first pivot-CSDN博客 第3讲 MIT_线性代数笔记&#xff1a;第 03…

反弹shell介绍和应用

一、什么是反弹shell 1 .含义 反向连接弹shell(即反弹shell为攻击者为服务端,受害者主机为客户端主动连接攻击者的服务端) 2 .目的 对方主机在外网无法访问 对方主机防火墙限制,只能发送请求,不能接收请求 对方IP动态变化 攻击了一台主机需要在自己的机器上…

3D一览通助力成都派铂宇航航天管道设计交付

在航空航天这一高精尖行业中&#xff0c;每一处细节都承载着不可估量的责任与使命。特别是在航天航空管道设计制造的复杂供应链中&#xff0c;任何误差都可能引发连锁反应&#xff0c;影响整体性能乃至安全。当前&#xff0c;航空航天行业面临的一大挑战在于如何有效管理这一复…

【数据结构】LinkedList ------ java实现

知识框架图&#xff1a; LinkedList是一种常用的数据结构。底层是一个双向链表。每个节点包含数据以及指向前一个节点和后一个节点的引用。 一&#xff1a;LinkedList的使用 1.1 LinkedList的构造方法 方法 解释LinkedList() 无参构造public LinkedList(Collection<? exte…

【教程】MySQL数据库学习笔记(六)——数据查询语言DQL(持续更新)

写在前面&#xff1a; 如果文章对你有帮助&#xff0c;记得点赞关注加收藏一波&#xff0c;利于以后需要的时候复习&#xff0c;多谢支持&#xff01; 【MySQL数据库学习】系列文章 第一章 《认识与环境搭建》 第二章 《数据类型》 第三章 《数据定义语言DDL》 第四章 《数据操…

Day16_0.1基础学习MATLAB学习小技巧总结(16)——元胞数组

利用空闲时间把碎片化的MATLAB知识重新系统的学习一遍&#xff0c;为了在这个过程中加深印象&#xff0c;也为了能够有所足迹&#xff0c;我会把自己的学习总结发在专栏中&#xff0c;以便学习交流。 素材来源“数学建模清风” 特此说明&#xff1a;本博客的内容只在于总结在…

详细讲解hive on tez中各个参数作用,以及如何优化sql

最近经常有优化sql的任务&#xff0c;但是自己能力有限&#xff0c;只能凭经验去优化&#xff0c;现整理加学习一波&#xff0c;也欢迎各位学习和讨论。 我们经常用hivesql 的模型就是 join.如下。 insert overwrite table a select * from b left join c 这里面发生了什么…

【C++取经之路】map的详细介绍及其使用

目录 关于map 键值对 map的常用操作 关于multimap 关于map template < class Key, // map::key_typeclass T, // map::mapped_typeclass Compare less<Key>, //…

【时间盒子】-【5.绘制闹钟】动态绘制钟表和数字时间

Tips: Preview装饰器&#xff0c;支持组件可预览&#xff1b; Component装饰器&#xff0c;自定义组件&#xff1b; Canvas组件的使用&#xff1b; 使用RenderingContext在Canvas组件上绘制图形&#xff0c;请参考官方文档&#xff1a;https://developer.huawei.com/consume…

Apache ShardingSphere数据分片弹性伸缩加解密中间件

Apache ShardingSphere Apache ShardingSphere 是一款分布式 SQL 事务和查询引擎,可通过数据分片、弹性伸缩、加密等能力对任意数据库进行增强。 软件背景 ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding…

如何利用python实现碰撞原理

先看图 跑了大概一天 这是结果 具体是通过BIP39规则生成的种子数据 生成完词组后&#xff0c;再根据词组生成姨太地址 # 生成随机助记词 def generate_mnemonic():entropy os.urandom(16) # 随机生成 16 字节熵mnemonic []for i in range(12): # 生成 12 个助记词word_in…

欧拉数据库的搭建及其部署

数据库的搭建 进行数据库安装前&#xff0c;必须保证软件yum仓库搭建完成 使用命令 dnf install mariadb-server&#xff0c;发现冲突selinux-policy-targeted-35.5-21.oe2203sp3.noarch有问题 [rootlocalhost yum.repos.d]# dnf install mariadb-server [rootlocalhost y…

Arthas工具使用,分析线上问题好帮手

在K8S中的步骤&#xff1a; #1.进入node #2.下载arthas 在容器中下载并启动 Arthas&#xff1a; java -jar arthas-boot.jar --repo-mirror aliyun --use-http #3.找到出现问题的类和方法的绝对路径 类路径 方法 #4.执行trace命令或者watch命令 trace:命令会追踪方法的执…

电脑硬盘数据丢失了怎么恢复?简单实用的硬盘数据找回的方法

我们的电脑使用硬盘作为存储设备来保存数据&#xff0c;硬盘里的数据是存储在扇区上&#xff0c;这些存储数据的单元则位于表面有磁性材料的旋转的盘片上。硬盘内部的磁头悬浮于高速旋转的盘片上&#xff0c;用于读写和检索数据。 假如我们使用电脑时不小心删除了某个文件&…

iOS——weak修饰符的学习补充

Weak修饰符的内部机制 SideTable ObjectC中对对象的存储&#xff0c;实现上做了一定的优化&#xff0c;一旦有弱引用对象被赋值&#xff0c;即运行时&#xff08;Runtime&#xff09;会在全局的SideTables中分配一个SideTable空间&#xff0c;此空间是根据对象的地址相关算法…

多线程 | synchronized的底层原理

目录 1.它做了什么2.什么是Monitor如何减少用户态和内核态的切换 3.对象头和内置锁 (ObjectMonitor)3.1对象头3.2内置锁 (ObjectMonitor)3.3wait方法底层3.4notify 方法的底层实现 4.总结 1.它做了什么 使用synchronized的修饰的代码块如下&#xff0c;那么jvm是如何编译它的&…