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

重温设计模式--命令模式

文章目录

      • 命令模式的详细介绍
      • C++ 代码示例
      • C++代码示例2

命令模式的详细介绍

  1. 定义与概念

    • 命令模式属于行为型设计模式,它旨在将一个请求封装成一个对象,从而让你可以用不同的请求对客户端进行参数化,将请求的发送者和接收者解耦,并且能够方便地对请求进行排队、记录请求日志,以及支持可撤销的操作等。
    • 例如,在一个智能家居系统中,有各种电器设备(如灯、电视、空调等),而用户可以通过遥控器(类似调用者)发出各种操作指令(如开灯、关电视、调空调温度等,这些指令就是不同的命令),每个电器设备就是接收者,它们知道如何具体执行对应的操作。通过命令模式,可以把这些操作指令都封装成一个个独立的命令对象,这样遥控器就可以方便地调用不同的命令来控制不同的电器,而且便于系统进行扩展、管理和实现诸如撤销操作等功能。
  2. 角色构成及职责

    • 命令(Command)接口或抽象类:这是整个模式的核心抽象,它声明了执行操作的方法,通常是一个名为 execute 的纯虚函数(在 C++ 中)。其作用是为所有具体命令类提供统一的执行接口规范,使得调用者可以用统一的方式来调用不同的具体命令。
    • 具体命令(ConcreteCommand)类:实现了 Command 接口,内部持有一个接收者(Receiver)对象的引用。在 execute 方法中,它会调用接收者对象相应的方法来完成具体的操作。例如,对于“开灯”这个具体命令,它的 execute 方法里就会调用灯(接收者)对象的“点亮”方法来实际执行开灯操作。
    • 接收者(Receiver)类:接收者是真正知道如何执行具体业务逻辑和操作的对象,它包含了与实际操作相关的方法。不同的接收者对应不同的功能实体,比如电器设备等,每个接收者的方法实现了具体要做的事情,像灯的亮灭、电视的开关频道切换等操作都是在接收者类里定义方法实现的。
    • 调用者(Invoker)类:负责触发命令的执行,它持有一个或多个命令对象的引用,可以通过调用命令对象的 execute 方法来让命令生效。调用者可以管理命令的执行顺序,例如可以将多个命令按顺序放入一个队列中然后依次执行;也能方便地实现一些高级功能,比如存储历史命令以便支持撤销和重做操作等。
      在这里插入图片描述
  3. 优点

    • 解耦请求发送者和接收者:发送者不需要知道接收者具体的实现细节以及如何执行操作,只需要调用命令对象的执行方法就行,这样双方的依赖关系变得松散,便于各自独立修改和扩展。
    • 方便实现撤销和重做功能:通过记录已经执行过的命令对象,可以很容易地实现撤销操作(按照一定规则反向执行之前的命令)以及重做操作(再次执行已经撤销的命令),这在很多应用场景中非常有用,比如文本编辑器的撤销和重做功能。
    • 增强代码的可扩展性和可维护性:新增加具体命令或者接收者都相对容易,只需要实现对应的接口或者继承相应的抽象类,然后按照规则整合到系统中即可,不会对现有代码结构造成大规模的破坏。
  4. 缺点

    • 增加了代码的复杂性:引入了多个类和接口来实现命令模式,相比于直接调用方法实现功能,整体代码结构变得更复杂,对于简单的场景来说可能有点“大材小用”,会让代码理解和维护成本在一定程度上提高。
    • 可能存在过多的小类:每一个具体的命令都需要对应一个具体命令类,如果有大量不同的命令,会导致类的数量增多,不过这可以通过合理的设计和适当的抽象来缓解。
  5. 应用场景

    • 图形界面操作:例如在绘图软件中,像绘制图形、移动图形、删除图形等操作都可以封装成不同的命令,方便用户通过菜单、快捷键等方式触发,也便于实现撤销和重做功能。
    • 游戏开发:游戏中角色的各种动作(如攻击、跳跃、移动等)可以看作是不同的命令,由玩家输入(调用者)触发,然后游戏角色(接收者)执行相应的动作,并且可以记录操作历史来实现一些回滚操作等功能。
    • 任务队列系统:把不同的任务封装成命令放入队列中,按照顺序依次执行,便于对任务进行统一管理和调度,比如后台服务器处理各种业务请求任务等场景。

C++ 代码示例

以下是一个简单的模拟遥控器控制电器设备的 C++ 代码示例,体现了命令模式的基本结构和用法:

#include <iostream>
#include <vector>
#include <memory>// 命令接口
class Command 
{
public:virtual void execute() = 0;
};// 接收者 - 灯类,代表一个可以被控制的电器设备
class Light
{
public:void turnOn(){std::cout << "Light is turned on." << std::endl;}void turnOff(){std::cout << "Light is turned off." << std::endl;}
};// 具体命令 - 开灯命令
class LightOnCommand : public Command 
{
private:std::shared_ptr<Light> light;
public:LightOnCommand(std::shared_ptr<Light> l) : light(l) {}void execute() override{light->turnOn();}
};// 具体命令 - 关灯命令
class LightOffCommand : public Command 
{
private:std::shared_ptr<Light> light;
public:LightOffCommand(std::shared_ptr<Light> l) : light(l) {}void execute() override{light->turnOff();}
};// 调用者 - 遥控器类
class RemoteControl 
{
private:std::vector<std::shared_ptr<Command>> onCommands;std::vector<std::shared_ptr<Command>> offCommands;
public:RemoteControl(){onCommands.resize(7);offCommands.resize(7);}void setCommand(int slot, std::shared_ptr<Command> onCommand, std::shared_ptr<Command> offCommand){onCommands[slot] = onCommand;offCommands[slot] = offCommand;}void onButtonWasPushed(int slot) {if (onCommands[slot]) {onCommands[slot]->execute();}}void offButtonWasPushed(int slot) {if (offCommands[slot]){offCommands[slot]->execute();}}
};int main()
{// 创建灯对象std::shared_ptr<Light> livingRoomLight = std::make_shared<Light>();// 创建具体命令对象std::shared_ptr<LightOnCommand> livingRoomLightOn = std::make_shared<LightOnCommand>(livingRoomLight);std::shared_ptr<LightOffCommand> livingRoomLightOff = std::make_shared<LightOffCommand>(livingRoomLight);// 创建遥控器对象RemoteControl remote;remote.setCommand(0, livingRoomLightOn, livingRoomLightOff);// 按下遥控器开灯按钮remote.onButtonWasPushed(0);// 按下遥控器关灯按钮remote.offButtonWasPushed(0);return 0;
}

在上述代码中:

  • Command 接口定义了统一的执行操作的抽象方法 execute
  • Light 类作为接收者,包含了灯的实际操作方法 turnOnturnOff
  • LightOnCommandLightOffCommand 是具体命令类,它们分别关联了灯对象,并在 execute 方法中调用对应的灯操作方法来实现开灯和关灯的命令功能。
  • RemoteControl 类作为调用者,通过 setCommand 方法可以设置不同按钮对应的开和关命令,然后通过 onButtonWasPushedoffButtonWasPushed 方法来触发相应命令的执行,模拟了遥控器控制灯的操作过程。

这个示例只是一个基础的展示,你可以根据实际需求进一步扩展,比如添加更多的电器设备和对应的命令,或者实现命令的撤销、重做等功能(就像前面介绍中提到的那样,可以通过记录已执行的命令列表等方式来实现)。

C++代码示例2

#include<iostream>
#include<list>
using namespace std;
//将一个请求封装成一个对象,从而使你可用不同的请求对客户进行参数化//厨师类
class C_COOK
{
public:virtual void docooking(){cout<<"111111111"<<endl;}
};//广东厨师
class GuangDongCook: public C_COOK
{
public:virtual void docooking(){cout<<"广东菜,淡、淡、淡"<<endl;}
};//四川厨师
class SiChuanCook: public C_COOK
{
public:virtual void docooking(){cout<<"四川菜,辣、辣、辣"<<endl;}
};//菜点
class Food
{
public:virtual void cook(){}
};//广东菜
class Guangdongfood : public Food
{
private:C_COOK *m_cook;
public:Guangdongfood(C_COOK *p_cook):m_cook(p_cook){}void cook(){m_cook->docooking();}
};//四川菜
class SiChuanfood : public Food
{
private:C_COOK *m_cook;
public:SiChuanfood(C_COOK *p_cook):m_cook(p_cook){}void cook(){m_cook->docooking();}
};//服务员
class Waiter
{list<Food*>ls;
public:void SetOrder(Food *p_food){ls.push_back(p_food);}void POST(){list<Food*>::iterator itr = ls.begin();for(;itr!=ls.end();++itr){std::cout<<typeid(*itr).name()<<endl;//打印出来类型,在这里还是Food *类型(*itr)->cook();//对应的师傅开始做菜//在此处调用开始出现多态,//第一次push进来的是  Food *sifood = new SiChuanfood(m_suicook);//实际类型是 SiChuanfood * 当调用时进行RTTI运行时类型识别 识别为SiChuanfood*//进而调用  cout<<"四川菜,辣、辣、辣"<<endl;}}
};int main()
{C_COOK *m_suicook = new SiChuanCook();C_COOK*m_gdcook = new GuangDongCook();Food *sifood = new SiChuanfood(m_suicook);Food*gdfood = new Guangdongfood(m_gdcook);Waiter xiaoli;xiaoli.SetOrder(sifood);//记录xiaoli.SetOrder(gdfood);//记录xiaoli.POST();//通知return 0;
}输出如下
class Food *
四川菜,辣、辣、辣
class Food *
广东菜,淡、淡、淡
请按任意键继续. . .

如果要是再增加一个湖南菜,这时需要加一个湖南菜的类和湖南厨师类,代码如下

#include<iostream>
#include<list>
using namespace std;
//将一个请求封装成一个对象,从而使你可用不同的请求对客户进行参数化//厨师类
class C_COOK
{
public:virtual void docooking(){cout<<"111111111"<<endl;}
};//广东厨师
class GuangDongCook: public C_COOK
{
public:virtual void docooking(){cout<<"广东菜,淡、淡、淡"<<endl;}
};//四川厨师
class SiChuanCook: public C_COOK
{
public:virtual void docooking(){cout<<"四川菜,辣、辣、辣"<<endl;}
};//湖南厨师
class HUnanCook: public C_COOK
{
public:virtual void docooking(){cout<<"湖南菜,贼辣、贼辣、贼辣"<<endl;}
};//菜点
class Food
{
public:virtual void cook(){}
};//广东菜
class Guangdongfood : public Food
{
private:C_COOK *m_cook;
public:Guangdongfood(C_COOK *p_cook):m_cook(p_cook){}void cook(){m_cook->docooking();}
};//四川菜
class SiChuanfood : public Food
{
private:C_COOK *m_cook;
public:SiChuanfood(C_COOK *p_cook):m_cook(p_cook){}void cook(){m_cook->docooking();}
};//新增//湖南菜
class Hunanfood : public Food
{
private:C_COOK *m_cook;
public:Hunanfood(C_COOK *p_cook):m_cook(p_cook){}void cook(){m_cook->docooking();}
};//服务员
class Waiter
{list<Food*>ls;
public:void SetOrder(Food *p_food){ls.push_back(p_food);}void POST(){list<Food*>::iterator itr = ls.begin();for(;itr!=ls.end();++itr){std::cout<<typeid(*itr).name()<<endl;//打印出来类型,在这里还是Food *类型(*itr)->cook();//在此处调用开始出现多态,//第一次push进来的是  Food *sifood = new SiChuanfood(m_suicook);//实际类型是 SiChuanfood * 当调用时进行RTTI运行时类型识别 识别为SiChuanfood*//进而调用  cout<<"四川菜,辣、辣、辣"<<endl;}}
};int main()
{C_COOK *m_suicook = new SiChuanCook();C_COOK*m_gdcook = new GuangDongCook();C_COOK*m_hncook = new HUnanCook();Food *sifood = new SiChuanfood(m_suicook);Food*gdfood = new Guangdongfood(m_gdcook);Food*hnfood = new Hunanfood(m_hncook);Waiter xiaoli;xiaoli.SetOrder(sifood);xiaoli.SetOrder(gdfood);xiaoli.SetOrder(hnfood);xiaoli.POST();return 0;
}结果如下
class Food *
四川菜,辣、辣、辣
class Food *
广东菜,淡、淡、淡
class Food *
湖南菜,贼辣、贼辣、贼辣
请按任意键继续. . .

相关文章:

重温设计模式--命令模式

文章目录 命令模式的详细介绍C 代码示例C代码示例2 命令模式的详细介绍 定义与概念 命令模式属于行为型设计模式&#xff0c;它旨在将一个请求封装成一个对象&#xff0c;从而让你可以用不同的请求对客户端进行参数化&#xff0c;将请求的发送者和接收者解耦&#xff0c;并且能…...

电力通信规约-104实战

电力通信规约-104实战 概述 104规约在广泛应用于电力系统远动过程中&#xff0c;主要用来进行数据传输和转发&#xff0c;本文将结合实际开发实例来讲解104规约的真实使用情况。 实例讲解 因为个人技术栈是Java&#xff0c;所以本篇将采用Java实例来进行讲解。首先我们搭建一…...

什么是事务

在数据库管理系统中&#xff0c;事务&#xff08;Transaction&#xff09;是执行一系列操作的最小工作单元&#xff0c;这些操作要么全部成功&#xff0c;要么全部失败。为了确保数据的一致性和完整性&#xff0c;事务被设计为具备四大特性&#xff0c;即原子性&#xff08;Ato…...

数据结构:双向循坏链表

目录 1.1双向循环链表的结构 2.双向链表功能的实现 2.1初始化链表 2.2销毁链表 2.3创建结点 2.4打印链表 2.5链表查找 2.6链表在pos的前面进行插入 2.7链表删除pos位置的节点 2.8链表的头插&#xff0c;头删 &#xff0c;尾插&#xff0c;尾删 1.1双向循环链表的结构 …...

3.1、SDH的5种标准容器

1、定义与作用 在 SDH&#xff08;同步数字体系&#xff09;中&#xff0c;标准容器&#xff08;C&#xff09;是一种用来装载各种速率的 PDH&#xff08;准同步数字系列&#xff09;信号的信息结构。它的主要作用是进行速率适配&#xff0c;使不同速率的 PDH 信号能够在 SDH 的…...

Jenkins介绍

Jenkins 是一款流行的开源自动化服务器&#xff0c;在软件开发和持续集成 / 持续交付&#xff08;CI/CD&#xff09;流程中发挥着关键作用。 一、主要功能 1.持续集成&#xff08;CI&#xff09; &#xff08;1&#xff09;.自动构建&#xff1a;Jenkins 可以配置为监听代码仓…...

5G学习笔记之Non-Public Network

目录 0. NPN系列 1. 概述 2. SNPN 2.1 SNPN概述 2.2 SNPN架构 2.3 SNPN部署 2.3.1 完全独立 2.3.2 共享PLMN基站 2.3.3 共享PLMN基站和PLMN频谱 3. PNI-NPN 3.1 PNI-NPN概述 3.2 PNI-NPN部署 3.2.1 UPF独立 3.2.2 完全共享 0. NPN系列 1. NPN概述 2. NPN R18 3. 【SNPN系列】S…...

网页生成鸿蒙App

如何网页生成鸿蒙App 纯鸿蒙发布后&#xff0c;鸿蒙App需求上升。如何快速生成鸿蒙App。变色龙云(http://www.appbsl.cn)推出了鸿蒙App打包服务。可以在线自动打包鸿蒙App。 第一步 创建应用 输入网站网址&#xff0c;上传图标。 第二步 生成鸿蒙证书 打开华为开发者管理中…...

JavaWeb通过Web查询数据库内容:(pfour_webquerymysql)

JavaWeb通过Web查询数据库内容&#xff1a; 数据库&#xff1a; 自行建库建表&#xff0c;主键 id 后端&#xff1a; 新建项目模块选择模块&#xff0c;添加依赖创建配置文件&#xff1a; db.propertiesJava类&#xff1a; query查询 前端&#xff1a; Web添加创建query.html…...

将java项目部署到linux

命令解析 Dockerfile: Dockerfile 是一个文本文件&#xff0c;包含了所有必要的指令来组装&#xff08;build&#xff09;一个 Docker 镜像。 docker build: 根据 Dockerfile 或标准指令来构建一个新的镜像。 docker save: 将本地镜像保存为一个 tar 文件。 docker load: 从…...

moviepy将图片序列制作成视频并加载字幕 - python 实现

DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 需要更多数据资源和技术解决方案&#xff0c;知识星球&#xff1a; “DataBall - X 数据球(free)” -------------------------------------------------------------…...

ROS1入门教程5:简单行为处理

一、新建项目 # 创建工作空间 mkdir -p demo5/src && cd demo5# 初始化工作空间 catkin_make# 创建功能包 cd src catkin_create_pkg demo roscpp actionlib_msgs message_generation tf 二、创建行为 # 创建行为目录 mkdir action && cd action# 创建行为文…...

Vue:实现输入框不能输负数功能

1、使用v-model指令 <input type"number" v-model"value" min"0" input"checkInput"> checkInput() {this.value Math.max(0, parseInt(this.value)); } 2、使用计算属性 <template><div><input type"…...

管理系统、微信小程序类源码文档-哔哩哔哩教程同步

文章目录 前言通用表基于JavaSpringBootVue前后端分离手机销售商城系统设计实现:基于JavaSpringBootVueuniapp实现大学生校园兼职微信小程序更新中。。。评论区打出你的题目 &#x1f308;你好呀&#xff01;我是 山顶风景独好 &#x1f388;欢迎踏入我的博客世界&#xff0c;能…...

AOP切点表达式之方法表达式execution

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…...

clickhouse-题库

1、clickhouse介绍以及架构 clickhouse一个分布式列式存储数据库&#xff0c;主要用于在线分析查询 2、列式存储和行式存储有什么区别&#xff1f; 行式存储&#xff1a; 1&#xff09;、数据是按行存储的 2&#xff09;、没有建立索引的查询消耗很大的IO 3&#xff09;、建…...

在 Sanic 应用中使用内存缓存管理 IP 黑名单

[外链图片转存中…(img-Pm0K9mzd-1734859380698)] 在现代 web 应用中&#xff0c;保护 API 接口免受恶意请求的攻击至关重要。IP 黑名单是一种常见的安全措施&#xff0c;可以有效阻止某些 IP 地址的访问。本文将介绍如何在 Python 的 Sanic 框架中实现 IP 黑名单功能&#xf…...

可翻折的CPCI导冷板卡插拔机构

本技术涉及一种cpci板卡模块拔插&#xff0c;尤其涉及一种可翻折的cpci导冷板卡插拔机构。 背景技术&#xff1a; 1、cpci(compactpci)导冷板卡是一种基于compactpci计算机总线标准的卡式电子模块&#xff0c;它在标准的cpci架构之上增加了导热板来提高散热能力&#xff0c;常…...

面试题整理9----谈谈对k8s的理解2

面试题整理9----谈谈对k8s的理解2 1. Service 资源1.1 ServiceClusterIPNodePortLoadBalancerIngressExternalName 1.2 Endpoints1.3 Ingress1.4 EndpointSlice1.5 IngressClass 2. 配置和存储资源2.1 ConfigMap2.2 Secret2.3 PersistentVolume2.4 PersistentVolumeClaim2.5 St…...

12个城市人文扫街、旅拍、人像风光摄影后期Lightroom调色预设

12个城市人文扫街、旅拍、人像风光摄影后期Lightroom调色预设 12 个专为城市场景设计的专业 Adobe Lightroom 预设。只需单击一下&#xff0c;即可通过一致、专业的基础简化您的编辑流程。 Lightroom & Lightroom Classic&#xff0c;桌面和移动兼容包括 12 张由 pat_kay…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

k8s从入门到放弃之Ingress七层负载

k8s从入门到放弃之Ingress七层负载 在Kubernetes&#xff08;简称K8s&#xff09;中&#xff0c;Ingress是一个API对象&#xff0c;它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress&#xff0c;你可…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

C++.OpenGL (10/64)基础光照(Basic Lighting)

基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)

RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发&#xff0c;后来由Pivotal Software Inc.&#xff08;现为VMware子公司&#xff09;接管。RabbitMQ 是一个开源的消息代理和队列服务器&#xff0c;用 Erlang 语言编写。广泛应用于各种分布…...