重温设计模式--命令模式
文章目录
- 命令模式的详细介绍
- C++ 代码示例
- C++代码示例2
命令模式的详细介绍
-
定义与概念
- 命令模式属于行为型设计模式,它旨在将一个请求封装成一个对象,从而让你可以用不同的请求对客户端进行参数化,将请求的发送者和接收者解耦,并且能够方便地对请求进行排队、记录请求日志,以及支持可撤销的操作等。
- 例如,在一个智能家居系统中,有各种电器设备(如灯、电视、空调等),而用户可以通过遥控器(类似调用者)发出各种操作指令(如开灯、关电视、调空调温度等,这些指令就是不同的命令),每个电器设备就是接收者,它们知道如何具体执行对应的操作。通过命令模式,可以把这些操作指令都封装成一个个独立的命令对象,这样遥控器就可以方便地调用不同的命令来控制不同的电器,而且便于系统进行扩展、管理和实现诸如撤销操作等功能。
-
角色构成及职责
- 命令(Command)接口或抽象类:这是整个模式的核心抽象,它声明了执行操作的方法,通常是一个名为
execute的纯虚函数(在 C++ 中)。其作用是为所有具体命令类提供统一的执行接口规范,使得调用者可以用统一的方式来调用不同的具体命令。 - 具体命令(ConcreteCommand)类:实现了
Command接口,内部持有一个接收者(Receiver)对象的引用。在execute方法中,它会调用接收者对象相应的方法来完成具体的操作。例如,对于“开灯”这个具体命令,它的execute方法里就会调用灯(接收者)对象的“点亮”方法来实际执行开灯操作。 - 接收者(Receiver)类:接收者是真正知道如何执行具体业务逻辑和操作的对象,它包含了与实际操作相关的方法。不同的接收者对应不同的功能实体,比如电器设备等,每个接收者的方法实现了具体要做的事情,像灯的亮灭、电视的开关频道切换等操作都是在接收者类里定义方法实现的。
- 调用者(Invoker)类:负责触发命令的执行,它持有一个或多个命令对象的引用,可以通过调用命令对象的
execute方法来让命令生效。调用者可以管理命令的执行顺序,例如可以将多个命令按顺序放入一个队列中然后依次执行;也能方便地实现一些高级功能,比如存储历史命令以便支持撤销和重做操作等。

- 命令(Command)接口或抽象类:这是整个模式的核心抽象,它声明了执行操作的方法,通常是一个名为
-
优点
- 解耦请求发送者和接收者:发送者不需要知道接收者具体的实现细节以及如何执行操作,只需要调用命令对象的执行方法就行,这样双方的依赖关系变得松散,便于各自独立修改和扩展。
- 方便实现撤销和重做功能:通过记录已经执行过的命令对象,可以很容易地实现撤销操作(按照一定规则反向执行之前的命令)以及重做操作(再次执行已经撤销的命令),这在很多应用场景中非常有用,比如文本编辑器的撤销和重做功能。
- 增强代码的可扩展性和可维护性:新增加具体命令或者接收者都相对容易,只需要实现对应的接口或者继承相应的抽象类,然后按照规则整合到系统中即可,不会对现有代码结构造成大规模的破坏。
-
缺点
- 增加了代码的复杂性:引入了多个类和接口来实现命令模式,相比于直接调用方法实现功能,整体代码结构变得更复杂,对于简单的场景来说可能有点“大材小用”,会让代码理解和维护成本在一定程度上提高。
- 可能存在过多的小类:每一个具体的命令都需要对应一个具体命令类,如果有大量不同的命令,会导致类的数量增多,不过这可以通过合理的设计和适当的抽象来缓解。
-
应用场景
- 图形界面操作:例如在绘图软件中,像绘制图形、移动图形、删除图形等操作都可以封装成不同的命令,方便用户通过菜单、快捷键等方式触发,也便于实现撤销和重做功能。
- 游戏开发:游戏中角色的各种动作(如攻击、跳跃、移动等)可以看作是不同的命令,由玩家输入(调用者)触发,然后游戏角色(接收者)执行相应的动作,并且可以记录操作历史来实现一些回滚操作等功能。
- 任务队列系统:把不同的任务封装成命令放入队列中,按照顺序依次执行,便于对任务进行统一管理和调度,比如后台服务器处理各种业务请求任务等场景。
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类作为接收者,包含了灯的实际操作方法turnOn和turnOff。LightOnCommand和LightOffCommand是具体命令类,它们分别关联了灯对象,并在execute方法中调用对应的灯操作方法来实现开灯和关灯的命令功能。RemoteControl类作为调用者,通过setCommand方法可以设置不同按钮对应的开和关命令,然后通过onButtonWasPushed和offButtonWasPushed方法来触发相应命令的执行,模拟了遥控器控制灯的操作过程。
这个示例只是一个基础的展示,你可以根据实际需求进一步扩展,比如添加更多的电器设备和对应的命令,或者实现命令的撤销、重做等功能(就像前面介绍中提到的那样,可以通过记录已执行的命令列表等方式来实现)。
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 命令模式的详细介绍 定义与概念 命令模式属于行为型设计模式,它旨在将一个请求封装成一个对象,从而让你可以用不同的请求对客户端进行参数化,将请求的发送者和接收者解耦,并且能…...
电力通信规约-104实战
电力通信规约-104实战 概述 104规约在广泛应用于电力系统远动过程中,主要用来进行数据传输和转发,本文将结合实际开发实例来讲解104规约的真实使用情况。 实例讲解 因为个人技术栈是Java,所以本篇将采用Java实例来进行讲解。首先我们搭建一…...
什么是事务
在数据库管理系统中,事务(Transaction)是执行一系列操作的最小工作单元,这些操作要么全部成功,要么全部失败。为了确保数据的一致性和完整性,事务被设计为具备四大特性,即原子性(Ato…...
数据结构:双向循坏链表
目录 1.1双向循环链表的结构 2.双向链表功能的实现 2.1初始化链表 2.2销毁链表 2.3创建结点 2.4打印链表 2.5链表查找 2.6链表在pos的前面进行插入 2.7链表删除pos位置的节点 2.8链表的头插,头删 ,尾插,尾删 1.1双向循环链表的结构 …...
3.1、SDH的5种标准容器
1、定义与作用 在 SDH(同步数字体系)中,标准容器(C)是一种用来装载各种速率的 PDH(准同步数字系列)信号的信息结构。它的主要作用是进行速率适配,使不同速率的 PDH 信号能够在 SDH 的…...
Jenkins介绍
Jenkins 是一款流行的开源自动化服务器,在软件开发和持续集成 / 持续交付(CI/CD)流程中发挥着关键作用。 一、主要功能 1.持续集成(CI) (1).自动构建: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 纯鸿蒙发布后,鸿蒙App需求上升。如何快速生成鸿蒙App。变色龙云(http://www.appbsl.cn)推出了鸿蒙App打包服务。可以在线自动打包鸿蒙App。 第一步 创建应用 输入网站网址,上传图标。 第二步 生成鸿蒙证书 打开华为开发者管理中…...
JavaWeb通过Web查询数据库内容:(pfour_webquerymysql)
JavaWeb通过Web查询数据库内容: 数据库: 自行建库建表,主键 id 后端: 新建项目模块选择模块,添加依赖创建配置文件: db.propertiesJava类: query查询 前端: Web添加创建query.html…...
将java项目部署到linux
命令解析 Dockerfile: Dockerfile 是一个文本文件,包含了所有必要的指令来组装(build)一个 Docker 镜像。 docker build: 根据 Dockerfile 或标准指令来构建一个新的镜像。 docker save: 将本地镜像保存为一个 tar 文件。 docker load: 从…...
moviepy将图片序列制作成视频并加载字幕 - python 实现
DataBall 助力快速掌握数据集的信息和使用方式,会员享有 百种数据集,持续增加中。 需要更多数据资源和技术解决方案,知识星球: “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实现大学生校园兼职微信小程序更新中。。。评论区打出你的题目 🌈你好呀!我是 山顶风景独好 🎈欢迎踏入我的博客世界,能…...
AOP切点表达式之方法表达式execution
天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。…...
clickhouse-题库
1、clickhouse介绍以及架构 clickhouse一个分布式列式存储数据库,主要用于在线分析查询 2、列式存储和行式存储有什么区别? 行式存储: 1)、数据是按行存储的 2)、没有建立索引的查询消耗很大的IO 3)、建…...
在 Sanic 应用中使用内存缓存管理 IP 黑名单
[外链图片转存中…(img-Pm0K9mzd-1734859380698)] 在现代 web 应用中,保护 API 接口免受恶意请求的攻击至关重要。IP 黑名单是一种常见的安全措施,可以有效阻止某些 IP 地址的访问。本文将介绍如何在 Python 的 Sanic 框架中实现 IP 黑名单功能…...
可翻折的CPCI导冷板卡插拔机构
本技术涉及一种cpci板卡模块拔插,尤其涉及一种可翻折的cpci导冷板卡插拔机构。 背景技术: 1、cpci(compactpci)导冷板卡是一种基于compactpci计算机总线标准的卡式电子模块,它在标准的cpci架构之上增加了导热板来提高散热能力,常…...
面试题整理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 预设。只需单击一下,即可通过一致、专业的基础简化您的编辑流程。 Lightroom & Lightroom Classic,桌面和移动兼容包括 12 张由 pat_kay…...
空调智慧节能控制系统解决方案:一键部署,适配多场景节能需求
一、应用背景 当前,建筑能耗已成为社会总能耗的重要组成部分,其中空调系统能耗占比高达50%左右,尤其在商业综合体、高校、酒店、写字楼等大型建筑中,空调能耗过高、管理粗放的问题尤为突出。传统空调控制系统依赖人工操作…...
AlphaFold实战指南:如何利用Colab+开源代码复现蛋白质结构预测(避坑版)
AlphaFold实战指南:如何利用Colab开源代码复现蛋白质结构预测(避坑版) 蛋白质结构预测一直是计算生物学领域的圣杯级难题。2021年DeepMind发布的AlphaFold2以原子级精度解决了这一挑战,彻底改变了结构生物学的研究范式。本文将带你…...
OpenClaw移动端管理:百川2-13B-4bits模型任务远程监控方案
OpenClaw移动端管理:百川2-13B-4bits模型任务远程监控方案 1. 为什么需要移动端管理OpenClaw任务? 去年冬天的一个深夜,我正在外地出差,突然接到同事紧急消息:"那个自动生成周报的脚本好像卡住了,客…...
探索Comsol弱形式求解三维光子晶体能带
Comsol弱形式求解三维光子晶体能带。在光子学领域,三维光子晶体能带的研究至关重要。而Comsol作为一款强大的多物理场仿真软件,其弱形式求解方法为我们探索三维光子晶体能带提供了有效途径。 光子晶体与能带理论基础 光子晶体是一种具有周期性介电结构的…...
西门子io-link
IO-Link 概述 https://www.ad.siemens.com.cn/download/materialaggregation_1459.html#-1459 IO-Link 概述 定义: IO-Link 是一种创新型点到点通信接口,适用于符合 IEC 61131-9 标准的传感器/执行器应用领域。 IO-Link 包含以下系统组件:…...
hadoop+spark+hive爬虫农产品推荐系统 农产品爬虫 农产品可视化 农产品价格预测系统 爬虫+线性回归预测算法+Flask框架
1、项目 介绍 技术栈: python语言、FLASK框架、requests爬虫技术、Echarts可视化、HTML、线性回归预测算法模型 惠农网https://www.cnhnb.com/农产品价格预测系统在现代农业领域发挥着重要作用,它不仅有助于农民合理安排农作物的种植和销售,…...
FreeSWITCH视频通话常见问题排查:编解码错误与媒体协商失败解决方案
FreeSWITCH视频通话故障排查手册:从编解码协商到媒体流修复 1. 视频通话架构与常见故障点全景 FreeSWITCH作为企业级通信平台的核心枢纽,其视频通话功能建立在SIP信令与RTP/RTCP媒体流的协同工作基础上。典型的视频通话故障通常出现在三个关键层面&#…...
如何利用Python自动化工具解决大麦网抢票难题:技术原理与实战配置
如何利用Python自动化工具解决大麦网抢票难题:技术原理与实战配置 【免费下载链接】ticket-purchase 大麦自动抢票,支持人员、城市、日期场次、价格选择 项目地址: https://gitcode.com/GitHub_Trending/ti/ticket-purchase 在当前热门演出票务市…...
3种破解方案:QMCDecode让QQ音乐加密格式限制成为历史
3种破解方案:QMCDecode让QQ音乐加密格式限制成为历史 【免费下载链接】QMCDecode QQ音乐QMC格式转换为普通格式(qmcflac转flac,qmc0,qmc3转mp3, mflac,mflac0等转flac),仅支持macOS,可自动识别到QQ音乐下载目录,默认转…...
从像素到概念:如何用Python+OpenCV一步步提取图像的底层和高层特征
从像素到概念:PythonOpenCV实战图像特征提取全流程 引言:理解图像特征的层次性 当我们注视一张海滩照片时,视觉系统会经历从局部到整体的认知过程——首先捕捉海浪的纹理、沙粒的明暗,然后识别出遮阳伞、游泳者等独立物体…...
