首页 > 编程语言 >Java设计模式之命令模式介绍和案例示范

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

时间:2024-09-13 18:53:05浏览次数:13  
标签:Java 请求 对象 void 模式 示范 命令 设计模式 public

一、命令模式简介

命令模式(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;
    }

    @Override
    public void execute() {
        receiver.createOrder();
    }
}

// 具体命令类 - 取消订单
class CancelOrderCommand implements OrderCommand {
    private OrderReceiver receiver;

    public CancelOrderCommand(OrderReceiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public 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 {
    @Override
    public void pay(int amount) {
        System.out.println("Paying " + amount + " using Alipay.");
    }
}

// 具体策略类 - 微信支付
class WeChatPayStrategy implements PaymentStrategy {
    @Override
    public 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 {

    @Bean
    public 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 {

    @Override
    public 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,请求,对象,void,模式,示范,命令,设计模式,public
From: https://blog.csdn.net/weixin_39996520/article/details/142153142

相关文章

  • Web安全之HTTPS调用详解和证书说明案例示范
    随着互联网的高速发展,网络安全成为了一个不可忽视的话题,特别是在涉及用户敏感信息的业务系统中。在此背景下,使用HTTPS取代HTTP成为了大势所趋。本文将以电商交易系统为例,详细介绍HTTPS的重要性,并探讨如何通过HTTPS来提升网站的安全性。第一章HTTPS的必要性问题:HTTP的不足......
  • java实现根据word模板赋值及电子签章实现
    一:添加相关依赖<!--电子签章实现<!&ndash;免费版.free只支持前三页转化&ndash;>--><dependency><groupId>e-iceblue</groupId><artifactId>spire.office.free</artifactId><......
  • JavaScript空值判断
    JavaScript本身没有判断一个变量是不是空值的函数,因为变量有可能是string,object,number,boolean等类型,类型不同,判断方法也不同。所以在文章中写了一个函数,用以判断JS变量是否空值,如果是undefined,null,'',NaN,false,0,[],{},空白字符串,都返回true,否则返回false。functionisEmpty(v){sw......
  • Java中异常的学习
    异常目录异常程序错误类型什么是异常?异常的主要作用?在实际的程序设计中,并非所有错误都能在编译期间被侦测到。运行时错误会引起异常(exception,也称例外):程序运行过程中出现的事件,它中断正常的程序控制流程。没有异常处理代码的程序,可能会非正常结束,有时候会引起严重问题。程序错......
  • Java 中实现动态代理
    在Java中,动态代理(DynamicProxy)允许在运行时创建代理对象来处理方法调用,而不需要在编译时定义具体的实现类。Java的java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口是实现动态代理的关键。步骤:创建接口:定义一个接口,代理对象将会实现这个接口......
  • Java笔试面试题AI答之单元测试JUnit(4)
    文章目录19.简述JUnitorg.junit.TestSuite类的作用?1.组织测试类2.简化测试执行3.灵活配置测试环境4.嵌套测试套件注意事项20.在JUnit中@Test注释的作用和用法?作用用法21.简述Junit基础注解(@BeforeClass、@Before、@Test、@After、@AfterClass)?22.编写代......
  • java学习9.13
    将java测试卷重新完成,测试完后基本完成需求,无明显BUG结合课堂上去写这个java测试卷,总的来说,之前没有独立写过类似项目+限时是比较大的问题。如果之前没有经历类似的情况,很多功能都是第一次用,那么就会导致出现bug而不知道如何去改,并且加上时间限制,如果时间全花在改bug上,又无法完......
  • A-计算机毕业设计定制:93904 家庭健康管理系统(免费领源码)可做计算机毕业设计JAVA、PHP
    摘 要随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,家庭健康管理系统被用户普遍使用,为方便用户能够可以随时进行家庭健康管理系统的数据信息管理,特开发了SSM家庭健康管理系......
  • 2025最新:如何打造Java SpringBoot Vue房屋租赁管理系统?掌握房源管理,实现房东无忧!
    ✍✍计算机毕业编程指导师**⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流!⚡⚡Java、Python、微信小程序、大数据实战项目集⚡⚡文末......
  • 2025年JavaWeb技术打造社区老人健康管理系统,3步轻松设计与实现,建议收藏!
    ✍✍计算机毕业编程指导师**⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流!⚡⚡Java、Python、微信小程序、大数据实战项目集⚡⚡文末......