设计模式之命令模式:从原理到实战,深入解析及源码应用

🎯 设计模式专栏,持续更新中
欢迎订阅:JAVA实现设计模式
🛠️ 希望小伙伴们一键三连,有问题私信都会回复,或者在评论区直接发言

命令模式

什么是命令模式?

命令模式(Command Pattern)是一种行为设计模式,它将一个请求封装为一个对象,从而允许使用不同的请求、队列或者日志来参数化对象,并支持可撤销的操作。命令模式的核心思想是将命令的发起者和执行者解耦,从而使得命令的发起者不必关心命令是如何被执行的。

命令模式的关键组成部分:

  • 命令(Command):定义命令的接口,声明执行方法。
  • 具体命令(Concrete Command):实现命令接口,绑定接收者,执行相关操作。
  • 接收者(Receiver):执行具体操作的类。
  • 调用者(Invoker):负责调用命令对象执行请求。
  • 客户端(Client):创建并配置具体的命令对象和接收者。

命令模式的UML原理类图

在这里插入图片描述

解释:

  • Command:命令接口,声明一个用于执行操作的execute()方法。
  • ConcreteCommandA:具体命令类,实现Command接口,并在execute()中调用接收者的某个动作。
  • Receiver:接收者类,负责具体的业务逻辑。
  • Invoker:调用者,持有命令对象,通过调用命令的execute()方法来执行请求。

生动案例——遥控器的设计

我们可以通过一个简单的遥控器来说明命令模式的实际应用。假设我们有一个遥控器,它可以控制不同的设备(如灯、电视、音响)。每个设备有开关功能,使用命令模式设计后,遥控器只需要发出命令,而不必关心设备是如何实现这些功能的。

代码实现

Step 1: 创建Command命令接口

public interface Command {
    // 执行命令
    void execute();
    // 撤销命令
    void undo();
}

Step 2: 创建Receiver接收者类,负责具体的业务逻辑

用上面的例子设计一个开灯关灯的执行逻辑

public class LightReceiver {
    public void on()
    {
        System.out.println("Light is on");
    }
    public void off()
    {
        System.out.println("Light is off");
    }
}

Step 3: 创建具体命令类

实现Command接口,并在execute()中调用接收者的某个动作

public class LightOnCommand implements Command{
    private LightReceiver lightReceiver;

    public LightOnCommand(LightReceiver lightReceiver) {
        this.lightReceiver = lightReceiver;
    }

    @Override
    public void execute() {
        lightReceiver.on();
    }

    @Override
    public void undo() {
        lightReceiver.off();
    }
}

public class LightOffCommand implements Command{
    private LightReceiver lightReceiver;

    public LightOffCommand(LightReceiver lightReceiver) {
        this.lightReceiver = lightReceiver;
    }
    public void execute() {
        lightReceiver.off();
    }
    public void undo() {
        lightReceiver.on();
    }
}
/**
 * 没有任何命令,即空执行: 用于初始化每个按钮, 当调用空命令时,对象什么都不做
 * 其实,这样是一种设计模式, 可以省掉对空判断
 * @author Administrator
 *
 */
public class NoCommand implements Command {

	@Override
	public void execute() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void undo() {
		// TODO Auto-generated method stub
		
	}

}

Step 4: 调用者(遥控器)

public class RemoteController {
    Command[] onCommands;
    Command[] offCommands;
    Command undoCommand;
    public RemoteController()
    {
        onCommands = new Command[5];
        offCommands = new Command[5];
        for (int i = 0; i < 5; i++)
        {
            onCommands[i] = new NoCommand();
            offCommands[i] = new NoCommand();
        }
    }

    public void setCommand(int no, Command onCommand, Command offCommand)
    {
        onCommands[no] = onCommand;
        offCommands[no] = offCommand;
    }

    public void onButtonWasPushed(int no)
    {
        onCommands[no].execute();
        undoCommand = onCommands[no];
    }

    public void offButtonWasPushed(int no)
    {
        offCommands[no].execute();
        undoCommand = offCommands[no];
    }

    public void undoButtonWasPushed()
    {
        undoCommand.undo();
    }


}

Step 5:客户端代码

public class Client {
    public static void main(String[] args)
    {
        //创建电灯的对象(接受者)
        LightReceiver lightReceiver = new LightReceiver();

        //创建电灯相关的开关命令
        LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
        LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);

        //需要一个遥控器
        RemoteController remoteController = new RemoteController();

        //给我们的遥控器设置命令, 比如 no = 0 是电灯的开和关的操作
        remoteController.setCommand(0, lightOnCommand, lightOffCommand);

        System.out.println("--------按下灯的开按钮-----------");
        remoteController.onButtonWasPushed(0);
        System.out.println("--------按下灯的关按钮-----------");
        remoteController.offButtonWasPushed(0);
        System.out.println("--------按下撤销按钮-----------");
        remoteController.undoButtonWasPushed();

    }
}

输出结果

--------按下灯的开按钮-----------
Light is on
--------按下灯的关按钮-----------
Light is off
--------按下撤销按钮-----------
Light is on

命令模式在 Spring 框架JdbcTemplate中的应用

Spring 框架中的 JdbcTemplate 是一个用于简化 JDBC 操作的工具类,它使用了命令模式来封装和管理数据库的操作。在 Spring 的设计中,JdbcTemplate 通过回调函数(或命令对象)来执行复杂的数据库操作,使得开发者可以将数据库连接、SQL 语句的执行以及资源的关闭等操作封装到具体的命令中。

下面我们从命令模式的角度,深入分析 JdbcTemplate 的源码

JdbcTemplate 的核心设计思想

JdbcTemplate 中,常见的数据库操作,比如查询、插入、更新等,都通过回调函数的形式传递给 JdbcTemplate,而具体的执行逻辑则由 JdbcTemplate 来处理。

  • 调用者(Invoker)JdbcTemplate
  • 命令接口(Command)PreparedStatementCallbackCallableStatementCallbackRowCallbackHandler 等回调接口
  • 具体命令(ConcreteCommand):开发者自定义的回调函数,或 Spring 提供的实现类
  • 接收者(Receiver)ConnectionPreparedStatementResultSet,这些是执行数据库操作的类

JdbcTemplate 通过这些回调接口,将数据库操作的细节交由具体的回调函数去执行,而自己则负责管理数据库连接、事务和资源的关闭

JdbcTemplate 中的命令模式实现

我们来看 JdbcTemplate 源码中是如何通过命令模式来实现数据库操作的。

代码示例:

JdbcTemplateexecute 方法为例:

public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action) throws DataAccessException {
    Assert.notNull(psc, "PreparedStatementCreator must not be null");
    Assert.notNull(action, "Callback object must not be null");
    Connection con = null;
    PreparedStatement ps = null;
    try {
        // 获取数据库连接
        con = DataSourceUtils.getConnection(getDataSource());
        // 创建 PreparedStatement
        ps = psc.createPreparedStatement(con);
        // 执行回调函数,执行具体的 SQL 操作
        return action.doInPreparedStatement(ps);
    }
    catch (SQLException ex) {
        throw getExceptionTranslator().translate("PreparedStatementCallback", getSql(psc), ex);
    }
    finally {
        closeStatement(ps);
        DataSourceUtils.releaseConnection(con, getDataSource());
    }
}

在这段代码中,命令模式的几个关键点体现如下:

  • 命令接口PreparedStatementCallback 是一个回调接口,定义了 doInPreparedStatement(PreparedStatement ps) 方法。该方法就是命令接口的 execute 方法,负责具体的数据库操作。
@FunctionalInterface
public interface PreparedStatementCallback<T> {
    T doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException;
}

具体命令PreparedStatementCallback 的实现类或匿名内部类就是具体的命令对象,它将实际的 SQL 操作封装在 doInPreparedStatement 方法中。

jdbcTemplate.execute(connection -> connection.prepareStatement("INSERT INTO user (name) VALUES (?)"), 
                     preparedStatement -> {
                         preparedStatement.setString(1, "John");
                         return preparedStatement.executeUpdate();
                     });

接收者PreparedStatementConnection 是数据库操作的接收者,它们执行具体的数据库操作(例如,SQL 的执行)。

调用者JdbcTemplate 是调用者,它负责管理数据库连接,调用命令对象(回调函数)并执行具体操作

JdbcTemplate 源码中的回调模式及命令模式应用场景

1. execute 方法

JdbcTemplateexecute 方法支持多种回调接口,例如:

  • PreparedStatementCallback:用于执行 SQL 语句并返回结果。
  • CallableStatementCallback:用于调用存储过程。

每种操作都通过回调的方式封装在命令中,然后由 JdbcTemplate 来统一管理连接和执行。

2. 查询操作:query 方法

JdbcTemplate 中的 query 方法用于查询数据库,并将结果集映射为对象。这也是命令模式的体现,开发者只需要提供一个 RowMapper(命令对象),负责将结果集转换为目标对象,其余的数据库操作细节由 JdbcTemplate 处理。

public <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException {
    return query(sql, new RowMapperResultSetExtractor<T>(rowMapper));
}

query 方法中,RowMapper 就是命令模式中的命令对象,ResultSet 是接收者,而 JdbcTemplate 是调用者,负责管理 SQL 的执行和结果集的处理。

JdbcTemplate 中,命令模式通过回调机制得到了很好的应用。命令模式的核心思想是解耦,将请求的发送者与执行者解耦,使得 JdbcTemplate 作为调用者不关心具体的 SQL 执行逻辑,而只负责连接管理、资源管理和事务处理。

总结

命令模式通过将请求封装为对象,使得命令发起者与执行者解耦,具有以下优点:

  • 解耦请求的发送者和接收者:发送者不需要知道如何处理请求,接收者可以自由变化。
  • 支持撤销和恢复:由于每个命令都被独立封装,命令可以保存并进行撤销或恢复。
  • 支持请求的记录、排队和日志:命令对象可以持久化,从而可以在需要时重新执行。

然而,命令模式也有一定的缺点:

  • 增加了系统复杂性:每一个操作都需要定义一个具体命令类,导致类的数量增多。

命令模式适用于需要对请求进行排队、撤销或者记录操作的场景,也是实现日志、事务等功能的有效设计模式。在实际开发中,我们可以根据需求灵活地应用命令模式,尤其是在复杂的业务逻辑和行为可变的系统中。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

sensitive-word 敏感词 v0.20.0 数字全部匹配,而不是部分匹配

敏感词系列 sensitive-word-admin 敏感词控台 v1.2.0 版本开源 sensitive-word-admin v1.3.0 发布 如何支持分布式部署&#xff1f; 01-开源敏感词工具入门使用 02-如何实现一个敏感词工具&#xff1f;违禁词实现思路梳理 03-敏感词之 StopWord 停止词优化与特殊符号 04-…

《微信小程序实战(3) · 推广海报制作》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

VISIA 皮肤检测

费用:自费158元 不能医保报销 先清洁肌肤,然后做一个皮肤检测. 1200万像素高清摄像头,一个白光,一个偏正光,还有一个紫外光,三种模式,分析面部情况. 8张图 反应皮肤情况应用: 在医美前和医美一次修复完成后,皮肤情况对比. 数值越高 越好 斑点图: 皱纹图: 分数比较低的话,皮肤…

SpringBoot教程(三十) | SpringBoot集成Shiro(权限框架)

SpringBoot教程&#xff08;三十&#xff09; | SpringBoot集成Shiro&#xff08;权限框架&#xff09; 一、 什么是Shiro二、Shiro 组件核心组件其他组件 三、流程说明shiro的运行流程 四、SpringBoot 集成 Shiro1. 添加 Shiro 相关 maven2. 添加 其他 maven3. 设计数据库表4.…

268页PPT大型集团智慧工厂信息化顶层架构设计(2024版)

智能制造装备是高端制造业的关键&#xff0c;通过整合智能传感、控制、AI等技术&#xff0c;具备了信息感知、分析规划等智能化功能&#xff0c;能显著提升加工质量、效率和降低成本。该装备是先进制造、信息、智能技术的深度融合。其原理主要包括物联网集成、大数据分析与人工…

【数据结构与算法 | 灵神题单 | 合并链表篇】力扣2, 21, 445, 2816

1. 力扣2&#xff1a;两数相加 1.1 题目&#xff1a; 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可…

黑神话悟空mac可以玩吗

黑神话悟空mac上能不能玩对于苹果玩家来说很重要&#xff0c;那么黑神话悟空mac可以玩吗&#xff1f;目前是玩不了了&#xff0c;没有针对ios系统的版本&#xff0c;只能之后在云平台上找找了&#xff0c;大家可以再观望下看看。 黑神话悟空mac可以玩吗 ‌使用CrossOver‌&…

JavaEE初阶——初识EE(Java诞生背景,CPU详解)

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯&#xff0c;你们的点赞收藏是我前进最大的动力&#xff01;&#xff01;希望本文内容能帮到你&#xff01; 目录 零&#xff1a;Java的发展背景介绍 一&#xff1a;EE的概念 二&#xff1a;计算机的构成 1&#xff1a;CU…

TCP 拥塞控制:一场网络数据的交通故事

从前有条“高速公路”&#xff0c;我们叫它互联网&#xff0c;而这条公路上的车辆&#xff0c;则是数据包。你可以把 TCP&#xff08;传输控制协议&#xff09;想象成一位交通警察&#xff0c;负责管理这些车辆的行驶速度&#xff0c;以防止交通堵塞——也就是网络拥塞。 第一…

你知道企业架构中核心的4大架构联系和不同吗?

引言&#xff1a;企业架构是指对企业信息管理系统中具有体系的、普遍性的问题而提供的通用解决方案它是基于业务导向和驱动的架构来理解、分析、设计、构建、集成、扩展、运行和管理信息统的。复杂系统是基于架构(或体系)的集成&#xff0c;而不是基于部件(或组件)的集成。指导…

【ARM】中断的处理

ARM的异常向量表 如果发生异常后并没有exception level切换&#xff0c;并且发生异常之 前使用的栈指针是SP_EL0&#xff0c;那么使用第一组异常向量表。如果发生异常后并没有exception level切换&#xff0c;并且发生异常之 前使用的栈指针是SP_EL1/2/3&#xff0c;那么使用第…

支付宝开发者✖️「蚂小财」——AgentUniverse专业多智能体框架在严谨产业中的应用实践

正在直播&#xff1a;点击进入直播间互动拿蚂蚁保温杯 &#xfeff;直播&#xfeff; &#xfeff;

英飞凌最新AURIX™TC4x芯片介绍

概述: 英飞凌推出最新的AURIX™TC4x系列,突破了电动汽车、ADAS、汽车e/e架构和边缘应用人工智能(AI)的界限。这一代面向未来的微控制器将有助于克服安全可靠的处理性能和效率方面的限制。客户将可缩短快速上市时间并降低整体系统成本。为何它被称为汽车市场新出现的主要颠覆…

828华为云征文 | 华为云Flexusx与Docker技术融合,打造个性化WizNote服务

前言 华为云Flexus X实例携手Docker技术&#xff0c;创新融合打造高效个性化WizNote服务。华为云Flexus X实例的柔性算力与Docker的容器化优势相结合&#xff0c;实现资源灵活配置与性能优化&#xff0c;助力企业轻松构建稳定、高效的云端笔记平台。828华为云企业上云节特惠来袭…

[2025]医院健康陪诊系统(源码+定制+服务)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

Element UI入门笔记(个人向)

Element UI入门笔记 将页面分割为一级菜单、二级菜单、导航栏三个部分&#xff1b;使用npm下载安装&#xff0c;使用语句npm i element-ui -s; 布局组件 el-form 用于创建和管理表单&#xff1b;从属性上看&#xff1a; :model&#xff1a;用于双向数据绑定&#xff0c;将表单…

Java语言程序设计基础篇_编程练习题*18.29(某个目录下的文件数目)

题目&#xff1a;*18.29(某个目录下的文件数目) 编写一个程序&#xff0c;提示用户输入一个目录&#xff0c;然后显示该目录下的文件数。 和上一题(18.28)的思路差不多&#xff0c;把找到文件后累加大小到变量变成计数1即可。 Java语言程序设计基础篇_编程练习题*18.28 (非递…

Linux(6)--CentOS目录

文章目录 1. 根目录2. cd目录切换命令3. CentOS目录介绍4. pwd命令介绍5. ls命令介绍5.1 ls5.2 ls -a5.3 ls -l 1. 根目录 Windows电脑的根目录是计算机(我的电脑)&#xff0c;然后C盘、D盘。 Linux系统的根目录是/&#xff0c;我们可以使用cd /进入根目录&#xff0c;然后使…

《深入理解JAVA虚拟机(第2版)》- 第12章 - 学习笔记

第12章 Java内存模型与线程 12.1 概述 TPS是用来衡量一个服务性能好坏高低的重要指标值。TPS是Transactions Per Second的缩写&#xff0c;用来表示每秒事务处理数&#xff0c;即服务端每秒平均能碰响应的请求数。 12.2 硬件的效率与一致性 处理器与内存的运算效率差了好几…

关于STM32项目面试题02:ADC与DAC篇(输入部分NTC、AV:0-5V、AI:4-20mA和DAC的两个引脚)

博客的风格是&#xff1a;答案一定不能在问题的后面&#xff0c;要自己想、自己背&#xff1b;回答都是最精简、最精简、最精简&#xff0c;可能就几个字&#xff0c;你要自己自信的展开。 面试官01&#xff1a;什么是模数转换/ADC&#xff1f;说说模数转换的流程&#xff1f; …