【搞懂设计模式】命令模式:从遥控器到编程的妙用!

我们都熟悉电视遥控器,它有许多按钮,每个按钮都有确定的功能。你按下电源键电视就会打开,再按下一次电视就会关闭。编程世界里也有这种模式,这就是我们说的命令模式。

命令模式是一种设计模式,它把一个请求或操作封装到一个对象中。这意味着命令模式允许我们将操作分开,我们可以控制何时和如何执行它们

遥控你的代码:命令模式的编程实战

了解命令模式的最好方式,就是通过代码来看看它如何运作。

假设我们要制作一个游戏模拟器,运行器可以执行两个命令,启动和关闭

// 游戏模拟器操作接口
interface Command {void execute();
}class GameConsole {public void start_game() {System.out.println("游戏开启!");}public void stop_game() {System.out.println("游戏关闭!");}
}// 启动命令
class StartCommand implements Command {GameConsole console;public StartCommand(GameConsole console) {this.console = console;}public void execute() {console.start_game();}
}// 停止命令
class StopCommand implements Command {GameConsole console;public StopCommand(GameConsole console) {this.console = console;}public void execute() {console.stop_game();}
}// 游戏模拟器
class GameSimulator {HashMap<String, Command> commands = new HashMap<>();public void register(String cmd_name, Command command) {commands.put(cmd_name, command);}public void execute(String cmd_name) {if (commands.containsKey(cmd_name)) {Command command = commands.get(cmd_name);command.execute();} else {System.out.println("Unknown command!");}}
}public class Main {public static void main(String[] args) {GameSimulator simulator = new GameSimulator();GameConsole console = new GameConsole();simulator.register("START", new StartCommand(console));simulator.register("STOP", new StopCommand(console));simulator.execute("START");simulator.execute("STOP");}
}

在上面的Java代码中,我们创建了一个游戏模拟器,它可以执行两种命令:启动和停止。我们使用了接口来定义Command,它只有一个execute方法。然后,我们创建了两个Command的实现类,即启动命令(StartCommand)和停止命令(StopCommand)。

命令模式如何实现撤销/恢复?

撤销和恢复的操作是通过以下两步实现的:

  1. 每次执行命令时,将该命令对象存储在历史记录列表中(如stack)
  2. 撤销操作其实就是从历史记录中取出最近的命令,并执行它的反操作。如果该命令对象中包含了执行前对象的状态信息,则撤销操作可以通过恢复这些状态来实现

让我们来看一个Java代码例子,这个例子展示了如何使用命令模式实现撤销功能:

interface Command {void execute();void undo();
}class AddTextCommand implements Command {private String textToAdd;private TextEditor editor;public AddTextCommand(String textToAdd, TextEditor editor) {this.textToAdd = textToAdd;this.editor = editor;}public void execute() {editor.addText(textToAdd);}public void undo() {editor.removeText(textToAdd);}
}class TextEditor {private StringBuilder text = new StringBuilder();public void addText(String textToAdd) {text.append(textToAdd);}public void removeText(String textToRemove) {int index = text.lastIndexOf(textToRemove);if (index != -1) {text.delete(index, index + textToRemove.length());}}public void printText() {System.out.println(text.toString());}
}class TextEditorDriver {private Stack<Command> commandHistory = new Stack<>();public void executeCommand(Command cmd) {cmd.execute();commandHistory.push(cmd);}public void undoLastCommand() {if (!commandHistory.isEmpty()) {Command lastCmd = commandHistory.pop();lastCmd.undo();}}
}

在这段代码中,我们首先定义了一个Command接口,并添加了一个新的方法undo。我们的每个具体命令都必须定义这两个方法。

在TextEditorDriver类中,我们维护了一个命令历史stack。每次执行命令时,我们都将命令添加到这个stack中。当我们要撤销操作时,我们只需取出最新的命令,并调用它的undo方法。

在命令模式中,撤销和恢复功能的实现核心是在每个具体的命令中,都保存了足够的信息以便在需要时可以反转其效果。通过使用存储的命令历史,我们不仅可以撤销命令,还可以重做它们。

命令模式的深入理解

以上的小例子虽然简单,但是已经显示了命令模式的三大优点

  • 解耦调用者与接收者:命令模式可以解耐调用者和接收者之间的耦合,将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
  • 命令可扩展:你可以增加新的命令而完全不改变现有的代码。
  • 支持撤销、队列、宏命令等:命令模式的另一个优点就是可以后期扩展新的特性。比如可以添加命令历史来实现撤销和恢复等功能,或者搭建任务队列进行后台处理等。

命令模式是创建型和行为型设计模式的交织,使用它,你就可以变得像使用遥控器一样自由自在地控制你的代码。

命令模式是创建型和行为型设计模式的交织怎么理解?

我们之前提到命令模式是创建型和行为型设计模式的交织。那么这句话是怎么理解的呢?

设计模式通常被分为三种类型:创建型,结构型和行为型。

  1. 创建型模式:这类模式主要处理对象创建机制,尝试在创建对象的过程中增加更多的灵活性和效率。简单工厂、抽象工厂、建造者、原型、单例等都是创建型模式。
  2. 结构型模式:这类模式主要关注对象组合,或者换句话说,实体之间如何互相使用。这些模式能保证系统中各部分之间相互关系的清晰地定义。适配器、桥接、组合、装饰、外观、享元、代理等都是结构型模式。
  3. 行为型模式:这类模式专注于对象间的通信,它们的主要目的是增强对象间的通信以及如何控制复杂系统中多个对象的协作。策略、模板方法、观察者、迭代器、责任链、命令、备忘录、状态、访问者等都是行为型模式。

命令模式是一个行为型模式,因为它解决的主要问题是将行为请求者和接收者进行解耦,实现请求的封装。这让请求者不必知道接收者的接口,也不必知道请求是怎么被接收的,以及操作是否被执行、是如何执行的,等等。

不过,命令模式也涉及创建型模式的一些特性:它涉及到具体命令类的创建以及如何和特定接收者实例相关联。每个具体的命令类都封装了特定的行为和调用接收者的相应方法。这样,每个命令可以看作是一个完整的操作

因此,命令模式是创建型模式和行为型模式的交织。它是创建型的,因为它创建了具有特定行为的命令对象。同时它是行为型的,因为这些对象被调用来执行特定的行为。

总结

理解和从实际角度看待设计模式非常重要,因为它们是我们进行有效编程和设计灵活可维护系统的工具。希望这次的讨论能帮助你更好地理解命令模式,明白它是如何综合创建型模式和行为型模式的。

当然,设计模式并不是银弹,应当根据具体场景和需求采取合适的设计模式。而这个能力则需要大量实践和经验的积累,加油!

如果上面的内容对你有帮助,请点赞收藏哦,我会分享更多的经验~

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

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

相关文章

以梦为码,CodeArts Snap 缩短我与算法的距离

背景 最近一直在体验华为云的 CodeArts Snap&#xff0c;逐渐掌握了使用方法&#xff0c;代码自动生成的准确程度大大提高了。 自从上次跟着 CodeArts Snap 学习用 Python 编程&#xff0c;逐渐喜欢上了 Python。 我还给 CodeArts Snap 起了一个花名&#xff1a; 最佳智能学…

千问 qwen7B 微调后的模型推理代码

watch -n 1 nvidia-smi 1、数据处理代码 import asttrain_dataset []# 定义合法的字段列表 valid_fields ["id", "conversations"]with open(train.json, r, encoding"utf-8") as f:train_data f.readlines() for i, item in enumerate(tra…

计算机服务器中了halo勒索病毒怎么办,halo勒索病毒解密处理流程

计算机技术的发展与应用为企业的生产生活提供了坚实基础&#xff0c;但同时也为网络安全威胁制造了有利条件。近期&#xff0c;网络上的勒索病毒非常嚣张&#xff0c;给企业的计算机服务器带来严重威胁。近日&#xff0c;云天数据恢复中心接到山东某制造公司的求助&#xff0c;…

Oracle DG环境下的秘钥管理

今天有朋友问到1&#xff09;DG环境下的秘钥管理需要注意什么&#xff0c;2&#xff09;秘钥管理对DG的日志同步有影响吗&#xff1f; 对于2&#xff09;的回答是明确的&#xff0c;没有影响。秘钥的管理和DG的redo log shipping完全是两套机制。在最新版的Oracle Key Vault常…

Qlik Sense : ErrorCode(错误变量)

错误变量 所有错误变量的值在脚本执行之后依然保留。第一个变量 ErrorMode 由用户输入&#xff0c;最后三个变量是 Qlik Sense 的输出&#xff08;包括脚本中错误的信息&#xff09;。 使用每个变量的下拉列表可查看每个变量的简短描述和语法。单击语法描述中的变量名称可了解…

Vulnhub靶机:FunBox10

一、介绍 运行环境&#xff1a;Virtualbox 攻击机&#xff1a;kali&#xff08;10.0.2.15&#xff09; 靶机&#xff1a;FunBox10&#xff08;10.0.2.35&#xff09; 目标&#xff1a;获取靶机root权限和flag 靶机下载地址&#xff1a;https://download.vulnhub.com/funbo…

Effective C++——关于重载赋值运算

令operator返回一个*this的引用 在重载,,*等运算符时&#xff0c;令其返回一个指向this的引用。 class MyClass {int* val; public:MyClass(int i) : val(new int(i)){}MyClass():val(new int(0)){}void print() {cout << *val << endl;}MyClass& operator(co…

mysql面试题合集-基础

前言 工作很忙&#xff0c;本质还是自己比较懒惰&#xff0c;很久没更新博客了。近期打算面试&#xff0c;换个工作环境&#xff0c;那就先从面试题开始吧&#xff0c;后续也会逐渐更新自己在工作中的一些经验感悟。接下来切入主题&#xff0c;由于长期做前台开发工作&#xf…

LabVIEW电液比例阀测试系统

电液比例阀与普通阀和伺服阀相比&#xff0c;比例阀展现出显著的耐污染和可靠性特点。为了满足这些比例阀的综合性能测试需求&#xff0c;开发了一种基于LabVIEW软件的电液比例阀综合性能试验台。这个系统不仅能够进行比例压力阀、流量阀和方向阀的性能测试&#xff0c;而且通过…

RabbitMQ 笔记一

概览&#xff1a; MQ基本概念 RabbitMQ入门 基本工作模 1.MQ是什么&#xff1f; MQ:Message Queue, 存储消息的中间件&#xff0c;是消息发送过程中的暂存容器&#xff0c;主要用于解决分布式系统进程间的通信。 分布式系统通信的两种方式&#xff1a;直接远程调用、借助第三…

01:云计算底层技术奥秘|虚拟化管理|公有云概述

云计算底层技术奥秘&#xff5c;虚拟化管理&#xff5c;公有云概述 虚拟化平台安装验证虚拟化支持 Linux虚拟机创建虚拟机磁盘虚拟机配置文件创建虚拟机 公有云简介 虚拟化平台安装 虚拟化&#xff1a;是一种技术 就是将不可拆分的实体资源变成可以自由划分的逻辑资源&#xf…

one-stage/two-stage区别

One-stage和Two-stage是目标检测中的两种主要方法&#xff0c;它们在处理速度和准确性上存在显著差异。以下是两者的主要区别&#xff1a; 处理流程&#xff1a;One-stage方法通过卷积神经网络直接提取特征&#xff0c;并预测目标的分类与定位&#xff0c;一步到位&#xff0c…

【数据库学习】PostgreSQL优化

1&#xff0c;数据库减负思路 缓存 页面静态化 实时性不高的数据&#xff1b;数据库优化sql优化 表结构优化 数据库分区分表 合并数据库操作&#xff0c;将多次操作合并成一条sql执行。热点数据分离 主表只保存活跃数据。数据库读写分离 2&#xff0c;执行计划 explain只…

美易官方:Edward Jones将特斯拉评级从买进下调至持有

近日&#xff0c;知名金融机构Edward Jones宣布将对特斯拉&#xff08;Tesla&#xff09;的评级进行调整&#xff0c;从买进下调至持有。这一消息引起了市场的广泛关注。特斯拉作为全球领先的电动汽车制造商&#xff0c;其股票一直备受投资者关注。本文将对此次评级调整进行分析…

【Go学习】Ginkgo测试框架学习实践 + 问题记录 + 怎么解决(0)

1、ginkgo测试框架介绍&#xff1a;https://onsi.github.io/ginkgo/ 2、重点是学习实践 问题记录 怎么解决 3、送福利&#xff1a;国内好用的ChatGpt有很多&#xff0c;比如&#xff1a;天工、文心一言、讯飞星火、通义万相等 1. 安装 xxxmacdeMacBook-Pro-3  /Volumes/mac…

【JS基础】定时器的使用、事件监听

文章目录 前言一、定时器1.1定时器是什么1.2 setInterval函数1.3 关闭定时器clearInterval 二、事件监听2.1 事件监听是什么2.2 事件监听的使用基本语法点击事件鼠标事件焦点事件键盘事件 2.3 事件对象event 总结 前言 JavaScript 中的定时器和事件监听是 Web 开发中至关重要的…

leetcode hot100组合

在本题中&#xff0c;是要求返回[1,n]这个数组的长度为k的组合。涉及到排列、组合、棋盘、分割等问题的时候&#xff0c;要考虑利用回溯来进行解决。 回溯和递归类似&#xff0c;也分为三步进行分析 确定递归函数的返回值和参数&#xff1a;一般来说返回值都是void&#xff0c…

黑豹程序员-vue实现两级联动下拉列表

需求 在开发中这类需求很多&#xff0c;前后两个下拉框有紧密关系&#xff0c;第一个下拉框相当于一个分类&#xff0c;选中第一个下拉框中的某个分类后&#xff0c;第二个下拉框的内容随之改变&#xff0c;列出其分类下的选项。 图例 选中某个一级风险领域后&#xff0c;二级…

在学校中,N个小朋友站成一队, 第i个小朋友的身高为height[i],,单调栈的应用

在学校中&#xff0c;N个小朋友站成一队&#xff0c; 第i个小朋友的身高为height[i]&#xff0c; 第i个小朋友可以看到的第一个比自己身高更高的小朋友j&#xff0c;那么j是i的好朋友(要求j > i)。 请重新生成一个列表&#xff0c;对应位置的输出是每个小朋友的好朋友位置&a…

时间数据前端显示格式化

背景 在实际我们通常需要在前端显示对数据操作的时间或者最近的更新时间&#xff0c;如果我们只是简单的使用 LocalDateTime.now()来传入数据不进行任何处理那么我们就会得到非常难看的数据 解决方式&#xff1a; 1). 方式一 在属性上加上注解&#xff0c;对日期进行格式…