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 接口轻松添加,无需修改现有代码。
九、总结

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

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

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

相关文章

学习Vue3的第四天

目录 pinia 安装 Pinia 存储读取数据 修改数据(三种方式) storeToRefs getters $subscribe store组合式写法 组件通信 props 自定义事件 mitt v-model $attrs $refs、$parent provide、inject slot pinia Pinia 是一个用于 Vue.js 的状态管理库,作…

vue-i18n 使用 $t 导致的 Typescript 报错问题

目录 1&#xff0c;问题2&#xff0c;解决 1&#xff0c;问题 在 Vue3 项目中使用 vue-i18n v9.14.0 时&#xff0c;可以&#xff1a; <template><div>{{ t(xxx) }}</div> </template><script setup lang"ts"> import { useI18n } f…

顶踩Emlog插件源码

源码介绍 顶踩Emlog插件源码 前些天看到小刀娱乐网的文章页面有了一些变化&#xff0c;那就是增加了一个有价值/无价值的顶踩按钮。 样式也是非常的好看 再加上两个表情包是非常的有趣。 写到了Emlog系统&#xff0c;效果如上图。 如何使用&#xff1a; 需要在echo_log.…

Datasheet SHT20芯片的数据手册

Datasheet SHT20芯片的数据手册 I2C读取湿度传感器返回的16位数据。SCL SDA 14位有效&#xff0c;我以为是将后二位删除&#xff0c;实际上看完手册才知道是后二位值无用&#xff0c;不是删除&#xff0c;而是清0&#xff0c;实际上还是16为&#xff0c;知识后二位是0还是1&…

Flume 日志采集系统

Flume 日志采集系统 一、Flume 概述二、Flume 架构设计2.1 架构图2.2 Flume Source 类型2.3 Flume Channel 类型2.4 Flume Sink 类型 三、Flume 安装部署3.1 下载解压3.2 上传解压3.3 修改配置文件2.4 启动 Flume Agent 四、案例实践&#xff1a;Flume 分布式集群搭建4.1 Flume…

基于SpringBoot的准妈妈孕期交流平台

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot框架 工具&#xff1a;IDEA/Eclipse、Navicat 系统展示 首页 管理员登录 用户管理 早教…

利用CubeMX复现正点原子TFTLCD驱动例程

来源&#xff1a;正点原子 FMC的工作原理暂时先欠着&#xff0c;先记录一下CRUD的过程。 第一步准备一个us级别延时函数&#xff0c;不会的参考拙作&#xff1a;STM32的定时器简介-CSDN博客 第二部开启FMC外设&#xff1a; ①进入 Pinout->FMC 配置栏&#xff0c;配置 …

如何做好网络安全

随着互联网技术的飞速发展&#xff0c;网站已成为企业对外展示、交流和服务的重要窗口。然而&#xff0c;随之而来的网站安全问题也日益凸显&#xff0c;给企业的业务发展和用户数据安全带来了巨大威胁。因此&#xff0c;高度重视网站安全已成为网络安全的首要任务。今天我们就…

使用ffmpeg在视频中绘制矩形区域

由于项目需要对视频中的人脸做定位跟踪&#xff0c; 我先使用了人脸识别算法&#xff0c;对视频中的每个帧识别人脸、通过人脸库比对&#xff0c;最终记录坐标等信息。 然后使用ffmpeg中的 drawbox 滤镜功能&#xff0c;选择性的绘制区域。从而实现人脸定位跟踪 1、drawbox …

用于协作代码开发的 10 大 GitHub 集成

GitHub 是开发人员的天堂。开发人员在分布式 GitHub 存储库中存储和管理其源代码,允许多个贡献者同时处理项目。这种协作行动将生产力提高了 22%,将修复漏洞的速度提高了 7 倍,并将入职时间缩短了 80%。 作为一个版本控制系统,它允许开发人员跟踪和审查更改、管理分支和合…

【Sceneform-EQR】通过sceneform-eqr实现一个视频播放器(使用安卓MediaPlayer实现视频播放)

在前一篇文档中介绍了如何在AR\三维场景创建几种背景 【Sceneform-EQR】scenefrom-eqr中的几种背景实现(不仅用于AR、三维场景&#xff0c;在图片、视频播放器中也适用) 本文将侧重介绍如何使用安卓MediaPlayer实现视频播放。 ↓↓↓↓↓↓↓↓↓↓↓↓ 以下正文 ↓↓↓↓↓↓…

李彦宏内部讲话曝光,谈大模型三大认知误区:智能体还是非共识

“外界对大模型有相当多的误解&#xff0c;”近日据媒体报道&#xff0c;李彦宏的一则内部讲话曝光。在最近一次和员工交流中&#xff0c;李彦宏谈及三个大模型认知误区&#xff0c;涵盖了大模型竞争、开源模型效率、智能体趋势等热点话题。 李彦宏认为未来大模型之间的差距可…

DWI扩散磁共振成像和结构连接组学指南

扩散磁共振成像和结构连接组学指南 引言流程概述扩散磁共振成像(dMRI)dMRI基础ADC&#xff08; apparent diffusion coefficient, 表观扩散系数&#xff09;MD&#xff08;mean diffusivity, 平均扩散率&#xff09;FA&#xff08; fractional anisotropy, 分数各向异性&#x…

“左侧文字横向”的QTabWidget

左侧用 QToolButton 组&#xff0c; 右侧用 QStackedWidget&#xff0c;信号槽绑定切换页面 可定制化高 QButtonGroup* btnGp new QButtonGroup(this);btnGp->addButton(ui->btn1, 0);btnGp->addButton(ui->btn2, 1);btnGp->addButton(ui->btn3, 2);connect…

MongoDB的Map-Reduce操作与聚合管道操作的两个实例相互转换

一、插入集合 comment 的文档的内容 二、题目要求 将集合 comment 中的文档进行聚合操作&#xff0c;即将字段 state为1的文档查询出来&#xff0c;然后按字段 nickname 进行分组,最后计算出每个评论者的评论条数。 三、mapReduce 操作代码 db.comment.mapReduce(// Map函数&…

linux搭建深度学习平台

linux搭建深度学习平台&#xff08;Ubuntu&#xff09; /home/guangyao/anaconda3 我服务器的anaconda地址 ~/anaconda3 1 首先就是打开浏览器&#xff0c;我实验室的是火狐&#xff0c;搜索anaconda下载&#xff0c;找到下载目录&#xff0c;cd进去&#xff0c; 2安装 bas…

鸿蒙界面开发——组件(6):属性字符串(StyledString)文本输入

属性字符串StyledString/MutableStyledString MutableStyledString继承于StyledString&#xff0c;以下统一简称StyledString。 是功能强大的标记对象&#xff0c;可用于字符或段落级别设置文本样式。 通过将StyledString附加到文本组件&#xff0c; 可以通过多种方式更改文本…

爆改YOLOv8|利用SCConv改进yolov8-即轻量又涨点

1&#xff0c;本文介绍 SCConv&#xff08;空间和通道重构卷积&#xff09;是一种高效的卷积模块&#xff0c;旨在优化卷积神经网络&#xff08;CNN&#xff09;的性能&#xff0c;通过减少空间和通道的冗余来降低计算资源的消耗。该模块由两个核心组件构成&#xff1a; 空间重…

ELK在Linux上部署教程

Docker Compose搭建ELK Elasticsearch默认使用mmapfs目录来存储索引。操作系统默认的mmap计数太低可能导致内存不足&#xff0c;我们可以使用下面这条命令来增加内存 sysctl -w vm.max_map_count262144创建Elasticsearch数据挂载路径 mkdir -p /echola/elasticsearch/data对…

【截图服务 +打包】pkg打包 puppeteer

目录 最后结论 windows打包成服务 定制executablePath 用程序来查找chrome.exe 代替上面的写配置文件 服务遇到的问题 使用java开一个线程启动 遇到的问题与解决 版本匹配问题 打出包后的运行报错问题 linux下的安装 安装n 库缺少 程序运行后的报错 制作 运行报…