当前位置: 首页 > 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方向的技术创新、技术突破及功能应用创…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...

在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?

uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...

三分算法与DeepSeek辅助证明是单峰函数

前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案

目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台

淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...