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

文章目录

      • 命令模式的详细介绍
      • 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 *
湖南菜,贼辣、贼辣、贼辣
请按任意键继续. . .

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/494459.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NavMeshAgent直接transform.position移动报错

对于NavMeshAgent组件&#xff0c;如果直接用transform.position移动位置会报错如下&#xff1a; xxx can only be called on an active agent that has been placed on a NavMesh。 需要使用如下方法进行移动位置&#xff0c;先不激活&#xff0c;移动完毕再激活。 using Sy…

272-1路万兆光纤SFP+和1路千兆网络 FMC子卡模块

一、概述 该板卡是基于kc705和ml605的fmc 10g万兆光纤扩展板设计&#xff0c;提供了1路万兆光纤SFP和1路千兆网络接口。可搭配我公司开发的FPGA载卡使用。载卡可参考&#xff1a;ID204 SFP&#xff08;10 Gigabit Small Form Factor Pluggable&#xff09;是一种可…

GitCode 光引计划投稿|JavaVision:引领全能视觉智能识别新纪元

在人工智能技术飞速发展的今天&#xff0c;计算机视觉作为AI领域的重要分支&#xff0c;正逐渐渗透到各行各业中。JavaVision&#xff0c;作为[光引计划]的一部分&#xff0c;致力于提供一个基于Java的全能视觉智能识别解决方案。同时它集成了MilvusPlus&#xff0c;旨在提供一…

如何在自己的云服务器上部署mysql

如何在自己的云服务器上部署mysql 前言&#xff1a; 我是用的是阿里云服务器&#xff0c;我的服务器上安装的系统是Ubuntu 20.04&#xff0c;一下内容都是居于此撰写。 前期准备工作 远程链接自己的云服务器&#xff0c;这里给大家推荐一个好用的软件&#xff1a;FinalShel…

华院计算参与项目再次被《新闻联播》报道

12月17日&#xff0c;央视《新闻联播》播出我国推进乡村振兴取得积极进展。其中&#xff0c;华院计算参与的江西省防止返贫监测帮扶大数据系统被报道&#xff0c;该系统实现了由原来的“人找人”向“数据找人”的转变&#xff0c;有效提升监测帮扶及时性和有效性&#xff0c;守…

UML图【重要】

文章目录 2.1 类图概述2.2 类图的作用2.3 类图表示法2.3.1 类的表示方式2.3.2 类与类之间关系的表示方式2.3.2.1 关联关系2.3.2.2 聚合关系2.3.2.3 组合关系2.3.2.4 依赖关系2.3.2.5 继承关系2.3.2.6 实现关系 统一建模语言&#xff08;Unified Modeling Language&#xff0c;U…

【数据科学导论】第一二章·大数据与数据表示与存储

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;数据处理与分析_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言…

问题解决:发现Excel中的部分内容有问题。是否让我们尽量尝试恢复? 如果您信任此工作簿的源,请单击“是”。

在开发同步导出功能是遇到了如标题所示的问题&#xff0c;解决后遂记录下来供大家参考。 RestController public class XxxController {PostMapping("/export")public BaseResponse export(RequestBody PolicyErrorAnalysisExportReq exportReq, HttpServletRespons…

软件设计与体系结构

1.简要说明什么是软件体系结构&#xff0c;软件体系结构模型&#xff0c;为什么要建立软件体系结构模型&#xff1f; 答&#xff1a;软件体系结构指一个软件系统在高层次上的结构化组织方式&#xff0c;包括系统的组成部分和各个部分之间的关系&#xff0c;以及它们与环境之间的…

开发手札:CameraRTS精准性优化

虽然三维软件提供了基本的物体RTS操作&#xff0c;但是对于用户来说过于复杂。 这些操作方式需要用户理解什么是三维空间、XYZ坐标系、欧拉角等。但是用户视角下&#xff0c;就一个二维屏幕动来动去的鼠标光标。 之前写过一套RTM组件&#xff0c;RTM组件&#xff0…

高级的SQL查询技巧有哪些?

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于高级SQL查询技巧方面的相关内容&#xf…

helm的介绍和安装

1 helm概述 1.1 资源对象难以管理的问题 helm是k8s资源清单的管理工具&#xff0c;它就像Linux下的包管理器&#xff0c;比如centos的yum&#xff0c;ubuntu的apt helm&#xff1a;命令行工具&#xff0c;主要用于k8s的chart的创建&#xff0c;打包&#xff0c;发布和管理。…

专业的内外网数据交换方案 可解决安全、效率、便捷3大问题

内外网数据交换是很多企业和行业都会面临的场景&#xff0c;既然隔离了内外网&#xff0c;重中之重就是要确保数据的安全性&#xff0c;其次在数据流转交换过程中&#xff0c;不能太繁琐复杂&#xff0c;需要让用户快速、便捷的进行数据交换。首先我们来看看&#xff0c;在进行…

2024 楚慧杯 re wp

go_bytes 附件拖入ida 输入长度为0x28&#xff0c;每两位字符的4bit拼接 与一个常量值经过运算后的值进行异或&#xff0c;并且判断是否相等 脚本 bouquet 附件拖入ida。简单去一下花 构建了一个二叉树&#xff0c;然后递归调用函数 重新排列一下再层序遍历读出即可 zistel 附件…

BERT模型入门(1)BERT的基本概念

文章目录 BERT是Bidirectional Encoder Representations from Transformers的首字母简写&#xff0c;中文意思是&#xff1a;Transformer的双向编码器表示。它是谷歌发布的最先进的嵌入模型。BERT在许多NLP任务中提供了更好的结果&#xff0c;如问答、文本生成、句子分类等&…

ECharts关系图-关系图11,附视频讲解与代码下载

引言&#xff1a; 关系图&#xff08;或称网络图、关系网络图&#xff09;在数据可视化中扮演着至关重要的角色。它们通过节点&#xff08;代表实体&#xff0c;如人、物体、概念等&#xff09;和边&#xff08;代表实体之间的关系或连接&#xff09;的形式&#xff0c;直观地…

java全栈day19--Web后端实战(java操作数据库3)

一、MyBatis 1.1介绍 前提引入&#xff1a; controller(控制层)作用&#xff1a;接受请求&#xff0c;响应数据 service(业务层)作用&#xff1a;负责具体的逻辑处理 dao(持久层)作用&#xff1a;数据访问层 一般的访问流程&#xff1a;浏览器发起请求过来&#xff0c;先…

Hmsc包开展群落数据联合物种分布模型分析通用流程(Pipelines)

HMSC&#xff08;Hierarchical Species Distribution Models&#xff09;是一种用于预测物种分布的统计模型。它在群落生态学中的应用广泛&#xff0c;可以帮助科学家研究物种在不同环境条件下的分布规律&#xff0c;以及预测物种在未来环境变化下的潜在分布范围。 举例来说&a…

PostgreSQL 的历史

title: PostgreSQL 的历史 date: 2024/12/23 updated: 2024/12/23 author: cmdragon excerpt: PostgreSQL 是一款功能强大且广泛使用的开源关系型数据库管理系统。其历史可以追溯到1986年,当时由加州大学伯克利分校的一个研究团队开发。文章将深入探讨 PostgreSQL 的起源、…

CSPM认证最推荐学习哪个级别?

一、什么是CSPM&#xff1f; CSPM的全称是Certified Strategic Project Manager&#xff0c;中文名称为“项目管理专业人员能力评价等级证书”。这是由中国标准化协会依据国家标准《项目管理专业人员能力评价要求》&#xff08;GB/T 41831-2022&#xff09;推出的一项认证&…