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

Java设计模式之命令模式介绍和案例示范

一、命令模式简介

命令模式(Command Pattern)是一种行为型设计模式,它将请求封装为一个对象,从而使你可以用不同的请求对客户端进行参数化、对请求排队或记录日志,以及支持可撤销的操作。命令模式的核心思想是将发出请求的对象与执行请求的对象分离,从而解耦请求的调用与处理逻辑。

在实际开发中,命令模式常用于实现事务管理、任务队列、操作撤销/重做等功能。该模式的关键优势在于它能够灵活地将不同的操作封装为命令对象,并通过组合、记录、重放等手段,实现复杂的操作逻辑。

二、命令模式的结构

命令模式的典型结构包括:

  1. Command:命令接口,声明了执行操作的接口。
  2. ConcreteCommand:具体命令类,实现了Command接口,负责定义请求的具体行为。
  3. Invoker:调用者,负责调用命令对象执行请求,通常持有一个或多个命令对象。
  4. Receiver:接收者,负责真正执行命令的逻辑。
  5. Client:客户端,负责创建命令对象,并将其与具体的接收者关联。

类图如下:
在这里插入图片描述

三、命令模式的使用场景
  1. 参数化请求:当系统需要对不同请求进行参数化时,可以使用命令模式将不同请求封装为独立的命令对象。
  2. 队列请求处理:当需要将请求放入队列中排队执行时,命令模式能够将请求对象化,从而方便地进行队列操作。
  3. 可撤销操作:通过命令模式,可以实现操作的撤销与重做功能。
四、命令模式的优缺点

优点

  • 松耦合:命令模式将请求的发出者与执行者解耦,降低了系统的耦合度。
  • 扩展性强:可以很容易地添加新的命令类来扩展系统的功能,而无需修改现有代码。
  • 支持撤销/重做:通过记录命令对象,可以方便地实现操作的撤销与重做功能。

缺点

  • 命令类数量增多:对于每一个具体操作都需要创建一个命令类,可能会导致类的数量增多,增加系统复杂性。
五、命令模式在电商交易系统中的应用

在电商交易系统中,命令模式可以用于以下场景:

  1. 订单操作的撤销与重做:假设系统需要支持订单操作的撤销与重做功能,可以使用命令模式将每个操作封装为一个命令对象,并记录这些操作,从而实现操作的撤销与重做。
  2. 任务队列的管理:在处理大量订单或库存更新时,可以使用命令模式将每个任务封装为命令对象,放入任务队列中按顺序执行,保证系统的稳定性和处理效率。

示例代码

// 命令接口
interface OrderCommand {void execute();
}// 接收者
class OrderReceiver {public void createOrder() {System.out.println("Creating order.");}public void cancelOrder() {System.out.println("Cancelling order.");}
}// 具体命令类 - 创建订单
class CreateOrderCommand implements OrderCommand {private OrderReceiver receiver;public CreateOrderCommand(OrderReceiver receiver) {this.receiver = receiver;}@Overridepublic void execute() {receiver.createOrder();}
}// 具体命令类 - 取消订单
class CancelOrderCommand implements OrderCommand {private OrderReceiver receiver;public CancelOrderCommand(OrderReceiver receiver) {this.receiver = receiver;}@Overridepublic void execute() {receiver.cancelOrder();}
}// 调用者
class OrderInvoker {private OrderCommand command;public void setCommand(OrderCommand command) {this.command = command;}public void executeCommand() {command.execute();}
}public class CommandPatternExample {public static void main(String[] args) {OrderReceiver receiver = new OrderReceiver();OrderCommand createOrder = new CreateOrderCommand(receiver);OrderCommand cancelOrder = new CancelOrderCommand(receiver);OrderInvoker invoker = new OrderInvoker();invoker.setCommand(createOrder);invoker.executeCommand();  // 输出: Creating order.invoker.setCommand(cancelOrder);invoker.executeCommand();  // 输出: Cancelling order.}
}

在这个示例中,CreateOrderCommandCancelOrderCommand 分别实现了创建订单和取消订单的功能。通过 OrderInvoker 类,客户端可以动态选择执行哪种命令,并且可以方便地实现操作的撤销与重做。

六、命令模式的常见问题和解决方式

问题1:命令类过多,导致系统复杂性增加

解决方式:可以通过命令工厂模式简化命令对象的创建过程,减少客户端直接操作命令对象的负担。此外,还可以将一些简单的命令类合并,减少类的数量。

问题2:撤销/重做功能实现复杂

解决方式:通过命令对象中保存操作的状态和参数,可以较容易地实现撤销和重做功能。此外,可以将命令对象与状态管理器结合,集中管理命令的撤销与重做操作。

问题3:命令对象的生命周期管理复杂

解决方式:通过引入命令队列或命令池来管理命令对象的生命周期,避免内存泄漏或对象重复创建带来的性能问题。

七、命令模式与策略模式的区别

命令模式与策略模式都是行为型设计模式,但它们有不同的应用场景和设计意图。

1. 命令模式 vs 策略模式

  • 目的
    • 命令模式旨在将请求封装为对象,从而使得不同的请求可以用相同的方式进行处理,并支持请求的撤销和重做。
    • 策略模式则用于定义一系列算法,将每个算法封装为一个策略类,并允许算法在运行时替换。
  • 应用场景
    • 命令模式适用于需要对请求进行排队、记录、撤销/重做的场景。
    • 策略模式适用于需要动态选择不同算法或行为的场景。
  • 结构
    • 命令模式通常包括命令类、接收者类、调用者类和客户端类。命令类负责封装请求,接收者类执行请求,调用者类触发命令的执行。
    • 策略模式通常包括策略接口、多个具体策略类和上下文类。上下文类负责管理策略对象,并在需要时调用策略对象执行具体的算法。

示例代码

// 策略接口
interface PaymentStrategy {void pay(int amount);
}// 具体策略类 - 支付宝支付
class AlipayStrategy implements PaymentStrategy {@Overridepublic void pay(int amount) {System.out.println("Paying " + amount + " using Alipay.");}
}// 具体策略类 - 微信支付
class WeChatPayStrategy implements PaymentStrategy {@Overridepublic void pay(int amount) {System.out.println("Paying " + amount + " using WeChat Pay.");}
}// 上下文类
class PaymentContext {private PaymentStrategy strategy;public void setStrategy(PaymentStrategy strategy) {this.strategy = strategy;}public void executePayment(int amount) {strategy.pay(amount);}
}public class StrategyPatternExample {public static void main(String[] args) {PaymentContext context = new PaymentContext();// 使用支付宝支付context.setStrategy(new AlipayStrategy());context.executePayment(100);  // 输出: Paying 100 using Alipay.// 使用微信支付context.setStrategy(new WeChatPayStrategy());context.executePayment(200);  // 输出: Paying 200 using WeChat Pay.}
}

在这个示例中,PaymentStrategy 定义了支付的策略接口,AlipayStrategyWeChatPayStrategy 分别实现了支付宝支付和微信支付的具体逻辑。通过策略模式,可以灵活地切换支付方式,而无需修改上下文类的代码。

八、命令模式在开源框架中的应用示范

1. Spring任务调度中的命令模式

在Spring框架中,任务调度器(Task Scheduler)是一个非常有用的工具,允许我们在特定的时间点或周期性地执行任务。通过将任务封装为一个命令对象,任务调度器可以在指定时间执行这些任务,这实际上就是命令模式的应用。

1.1 基本原理

在命令模式中,命令(Command)通常是一个实现了某种接口的对象,它封装了某个操作的所有信息。这个操作可以是某个具体的业务逻辑,比如发送一封邮件、生成一份报告等。在Spring的任务调度器中,这些命令通常实现了 Runnable 接口,并且可以由调度器在指定的时间执行。

1.2 代码示例

下面我们通过一个具体的代码示例来展示如何使用Spring的任务调度器实现命令模式。

1.2.1 准备工作

首先,我们需要在Spring应用中配置任务调度器。这里我们使用ConcurrentTaskScheduler,它是Spring提供的一个简单的任务调度器实现。

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;import java.util.Date;@Configuration
public class CommandPatternExample {@Beanpublic TaskScheduler taskScheduler() {return new ConcurrentTaskScheduler();  // 配置任务调度器}public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CommandPatternExample.class);TaskScheduler scheduler = context.getBean(TaskScheduler.class);// 创建命令对象Runnable command = new PrintTimeCommand();// 在5秒后执行命令scheduler.schedule(command, new Date(System.currentTimeMillis() + 5000));context.close();}
}

1.2.2 创建命令对象

接下来,我们定义一个实现 Runnable 接口的命令对象。这个对象封装了具体的操作逻辑,即在控制台上打印当前时间。

public class PrintTimeCommand implements Runnable {@Overridepublic void run() {System.out.println("Current time: " + new Date());}
}

1.2.3 运行示例

当你运行上述代码时,Spring任务调度器将在5秒后执行PrintTimeCommand命令,并输出当前的时间。输出示例如下:

Current time: Wed Aug 21 14:28:45 UTC 2024

通过这种方式,PrintTimeCommand 作为一个命令对象,被任务调度器在特定时间执行。这就是命令模式在Spring任务调度中的实际应用。

2. 命令模式的优势

通过上述示例可以看到,命令模式在Spring框架中有以下几个优势:

  • 解耦请求发送者和执行者:命令模式将任务的创建和执行解耦,使得我们可以独立地管理任务的执行时间和逻辑。
  • 灵活性高:我们可以根据需要创建不同的命令对象,并在调度器中灵活配置这些命令的执行时间。
  • 易于扩展:新的命令可以通过实现 Runnable 接口轻松添加,无需修改现有代码。
九、总结

命令模式通过将请求封装为对象,实现了请求的参数化、排队、记录和可撤销操作,在系统设计中具有广泛的应用场景。相比于策略模式,命令模式更适合处理请求的排队和管理,而策略模式则更注重算法的动态选择。两者在实际应用中各有侧重,开发者应根据具体需求选择合适的模式来实现系统的灵活性和扩展性。

相关文章:

Java设计模式之命令模式介绍和案例示范

一、命令模式简介 命令模式(Command Pattern)是一种行为型设计模式,它将请求封装为一个对象,从而使你可以用不同的请求对客户端进行参数化、对请求排队或记录日志,以及支持可撤销的操作。命令模式的核心思想是将发出请…...

Leetcode面试经典150题-74.搜索二维矩阵

解法都在代码里,不懂就留言或者私信 二分查找,比较简单 class Solution {/**解题思路:每一行有序、每一列也有序,只是整体不是严格有序的,那我们需要找一个点,只能往两个方向走,往一个方向走是…...

【数字集成电路与系统设计】基本的组合逻辑电路

目录 一、简单例子引入 1.1 端口声明 1.1.2 Verilog实现 1.1.3 Chisel实现 逐行解释 1.2 内部逻辑实现 1.2.1 Verilog实现 1.2.2 Chisel实现 Chisel 关键点解释 1.3 常用的硬件原语 二、Chisel主要数据类型介绍 2.1 数据类型 2.2 数据宽度 2.3 数据转换 2.4 运算…...

11. 建立你的第一个Web3项目

11. 建立你的第一个Web3项目 在这一部分,我们将带你一步步地建立一个简单的Web3项目,从环境搭建到智能合约的创建与部署,再到开发一个去中心化应用(dApp)并与智能合约交互。这是你迈向Web3开发的第一步。 1. 环境搭建…...

衡石分析平台使用手册-容器部署

容器部署​ 本文介绍如何在容器上部署 HENGSHI SENSE,以及部署后如何进行版本升级和数据备份。 部署前准备工作​ 单机部署前,请完成如下准备工作。 1.检查 docker 的环境。需要满足 Docker 版本 > 17.09安装 docker-compose。 2.获取并导入离线…...

静态库,动态库以及makefile基础

一.静态(链接)库 libfun.a 静态链接进可执行程序 可执行程序偏大 运行时只需要可执行程序即可 生成静态库步骤 gcc -c fun.c -o fun.o ar rcv libfun.a fun.o //需要用.o文件生成数据库 运行 gcc main.c libfun.a 二.动态库 libfun.so 动…...

Python基础语法(1)上

常量和表达式 我们可以把 Python 当成一个计算器,来进行一些算术运算。 print(1 2 - 3) print(1 2 * 3) print(1 2 / 3) 这里我们可能会有疑问,为什么不是1.6666666666666667呢? 其实在编程中,一般没有“四舍五入”这样的规则…...

使用 Python/java/go做一个微信机器人

E云是一套完整的的第三方服务平台,包含微信API服务、企微API服务、SCRM系统定制、企微系统定制、服务类软件定制等模块,本文档主要讲述个微API服务相关,以下简称API,它能处理用户微信中的各种事件,提供了开发者与个微对…...

【北京迅为】iTOP-i.MX6开发板使用手册第四部分固件编译第十四章非设备树Android4.4系统编译

可根据用户需求更换,百变定制,高端产品无忧! 迅为IMX6Q兼容四核商业级 、双核商业级、四核工业级 、更可提供i.MX6Q家族PLUS版本核心板。 核心板采用十层PCB沉金盲埋设计,更能保证电磁兼容与系统稳定。 公众号:迅为电…...

测评造假?Mistral首个多模态模型Pixtral 12B发布

测评造假?Mistral首个多模态模型Pixtral 12B发布! 近日,法国人工智能(AI)初创公司Mistral于9月11日宣布推出其首款多模态AI大模型——Pixtral 12B,成功吸引了全球科技界的广泛关注。这款集图像与文本处理能…...

【Java-简单练习题】

1.”AABBBCCC“>>"A2B3C3" public class Test6 {public static void main(String[] args) {String ns "AABBBCCCC";String retcompress(ns);System.out.println(ret);}public static String compress(String str) {StringBuilder ret new StringB…...

Notepad++ 下载安装教程

目录 1.下教程 2.安装教程 1.下教程 Downloads | Notepad (notepad-plus-plus.org) 进入下载地址后选择最新版点击连接 点击链接后,向下滑动,下载适合自己电脑版本的安装包 这里大家没有梯子可能打不开页面,可以直接从本文开头下载。 2.安…...

shader 案例学习笔记之smoothstep函数

参考:smoothstep 用来生成0-1的平滑过渡值 smoothstep函数源码实现: float smoothstep(float t1, float t2, float x) {// Scale, bias and saturate x to 0..1 rangex clamp((x - t1) / (t2 - t1), 0.0, 1.0); // Evaluate polynomialreturn x * x *…...

大模型的第一个杀手级应用场景出来了

大家终于都意识到大模型首先改变的是软件行业自己,而软件的根基是代码生成。代码生成第一波就是AI辅助开发,这个会是大模型第一个杀手级应用。大家苦苦逼问自己的大模型杀手级应用,为什么会是辅助编程,这里说下什么: 必…...

不允许有程序员不知道这款AI代码扩写工具

01CodeGeeX编程大模型 在介绍什么是codeGeeX之前,先上图。 想象一下,自己写代码的时候旁边有个专家助手,随时跟你解释前面别人写的代码是什么意思,有什么缺陷。在你自己写的时候也可以每一步进行代码提示和代码扩写,是…...

java 的list集合排序自定义元素

在 Java 中,可以对包含自定义元素的List集合进行排序。通常可以使用Collections.sort()方法结合自定义的比较器来实现。 一、定义包含自定义元素的类 假设我们有一个表示学生的类Student: class Student {private int id;private String name;private …...

【数学建模】2024数学建模国赛经验分享

文章目录 一、关于我二、我的数模历程三、经验总结: 一、关于我 我的CSDN主页:https://gxdxyl.blog.csdn.net/ 2020年7月(大二结束的暑假)开始在CSDN写作: 阿里云博客专家: 接触的领域挺多的&#xff…...

Scala尾递归解决爆栈问题

引言 我在上篇中详细的讲了递归的一系列问题,多路递归,爆栈问题,尾递归优化等,今天就实际演示一下尾递归是如何解决爆栈问题的,以及它的原理是什么? 支持尾递归优化的语言 尾递归是一种特殊的递归形式,如果…...

【观察者】设计模式:构建灵活且响应式的软件系统

引言 在软件开发中,我们经常面临需要在多个对象之间进行通信的挑战。特别是当一个对象的状态发生变化时,我们希望所有依赖于这个状态的对象都能自动更新。这就是观察者设计模式大显身手的地方。 简介 观察者模式是一种行为设计模式,它定义…...

开源网安斩获CCIA中国网络安全创新创业大赛总决赛三等奖

近日,由中央网信办指导,中国网络安全产业联盟(CCIA)主办的2024年中国网络安全创新创业大赛总决赛及颁奖典礼在国家网络安全宣传周落下帷幕。开源网安“AI代码审核平台CodeSec V4.0” 凭借在AI方向的技术创新、技术突破及功能应用创…...

uniapp 对接腾讯云IM群组成员管理(增删改查)

UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

JavaSec-RCE

简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性&#xff0c…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...

在树莓派上添加音频输入设备的几种方法

在树莓派上添加音频输入设备可以通过以下步骤完成,具体方法取决于设备类型(如USB麦克风、3.5mm接口麦克风或HDMI音频输入)。以下是详细指南: 1. 连接音频输入设备 USB麦克风/声卡:直接插入树莓派的USB接口。3.5mm麦克…...