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

embedded/2024/9/24 11:54:39/

命令模式

什么是命令模式

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

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

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

命令模式的UML原理类图

在这里插入图片描述

解释:

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

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

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

代码实现

Step 1: 创建Command命令接口

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

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

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

java">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()中调用接收者的某个动作

java">public class LightOnCommand implements Command{private LightReceiver lightReceiver;public LightOnCommand(LightReceiver lightReceiver) {this.lightReceiver = lightReceiver;}@Overridepublic void execute() {lightReceiver.on();}@Overridepublic 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 {@Overridepublic void execute() {// TODO Auto-generated method stub}@Overridepublic void undo() {// TODO Auto-generated method stub}}

Step 4: 调用者(遥控器)

java">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:客户端代码

java">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();}
}

输出结果

java">--------按下灯的开按钮-----------
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 方法为例:

java">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());// 创建 PreparedStatementps = 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 方法,负责具体的数据库操作。
java">@FunctionalInterface
public interface PreparedStatementCallback<T> {T doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException;
}

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

java">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.ppmy.cn/embedded/116082.html

相关文章

ubuntu 20.04 ‘Wired Unmanaged‘ 网络无法配置解决方法

问题描述 系统&#xff1a;ubuntu20.04连上网线后右上角没有有线网络连接的图标&#xff0c;在网络配置界面也只有VPN和无线网络的配置;实际上此时电脑已经连接网络&#xff0c;通过DHCP获得IP地址可以正常访问网络。 解决办法 ubuntu有有两套网络管理软件&#xff1a;serve…

STM32与51单片机的区别:是否应该直接学习STM32?

STM32与51单片机的区别&#xff1a;是否应该直接学习STM32&#xff1f; 在单片机的世界里&#xff0c;STM32和51单片机都是非常重要的角色。对于初学者来说&#xff0c;是否可以直接跳过51单片机&#xff0c;直接学习STM32&#xff0c;这个问题一直存在争议。让我们深入探讨这…

如何检测电脑有无恶意软件并处理掉?

检测并处理电脑上的恶意软件是维护计算机安全的重要步骤。以下是一些推荐的步骤&#xff1a; 检测恶意软件 使用杀毒软件&#xff1a; 安装一个可靠的杀毒软件&#xff0c;如360安全卫士、腾讯电脑管家、卡巴斯基、Bitdefender、Avast等。确保杀毒软件的病毒库是最新的。运行全…

切换笔记本键盘的启用与禁用状态

使用批处理脚本切换笔记本键盘的启用与禁用状态 背景描述详细步骤及代码解释1. 在管理员模式下运行脚本2. 脚本内容3. 解释 背景描述 在笔记本电脑中&#xff0c;在外接键盘的时候&#xff0c;有时我们希望禁用内置键盘&#xff0c;防止意外按键。Windows 系统中&#xff0c;键…

【Kubernetes】常见面试题汇总(三十三)

目录 85.简述 kube-proxy 的三种工作模式和原理。 特别说明&#xff1a; 题目 1-68 属于【Kubernetes】的常规概念题&#xff0c;即 “ 汇总&#xff08;一&#xff09;~&#xff08;二十二&#xff09;” 。 题目 69-113 属于【Kubernetes】的生产应用题。 85.简述 kub…

【深度学习】03-神经网络 3-3 梯度下降的优化方法-动量算法Momentum

常规的梯度下降算法中&#xff0c;会遇到平缓区域&#xff0c;碰到鞍点&#xff0c;碰到局部最小值&#xff08;截止当前无解&#xff09;&#xff0c;因此为了解决这个问题&#xff0c;我们需要优化传统的梯度下降算法。 动量算法&#xff08;Momentum&#xff09; 是梯度下降…

谈一谈 DDD

一、前言 最近 10 年的互联网发展,从电子商务到移动互联,再到“互联网+”与传统行业的互联网转型,是一个非常痛苦的转型过程。在这个过程中,一方面会给我们带来诸多的挑战,另一方面又会给我们带来无尽的机会,它会带来更多的新兴市场、新兴产业与全新业务,给我们带来全新…

Jetpack——Room

概述 Room是谷歌公司推出的数据库处理框架&#xff0c;该框架同样基于SQLite&#xff0c;但它通过注解技术极大简化了数据库操作&#xff0c;减少了原来相当一部分编码工作量。在使用Room之前&#xff0c;要先修改模块的build.gradle文件&#xff0c;往dependencies节点添加下…