设计模式——命令设计模式(行为型)

news2025/7/27 12:40:23

摘要

本文介绍了命令设计模式,这是一种行为型设计模式,用于将请求封装为对象,实现请求的解耦和灵活控制。它包含命令接口、具体命令、接收者、调用者和客户端等角色,优点是解耦请求发送者与接收者,支持命令的排队、记录、撤销等操作,但会增加系统复杂性。文中通过点餐系统类比说明其结构,并介绍了实现方式、适合场景和实战示例。

1. 命令设计模式定义

将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,队列或记录请求日志,以及支持可撤销的操作。命令模式将“命令的发起者”与“命令的执行者”解耦,通过封装命令对象来实现请求的解耦和灵活控制。

  • 优点:解耦请求发送者与接收者、支持命令的排队、记录、撤销/重做、更容易扩展新命令类
  • 缺点:增加了系统中类的数量,带来一定结构复杂性

1.1. 🧱 模式结构组成:

角色

说明

Command(命令接口)

声明执行操作的接口。

ConcreteCommand(具体命令)

实现命令接口,绑定接收者并调用其方法。

Receiver(接收者)

执行命令实际操作的类。

Invoker(调用者)

负责调用命令对象来执行请求。

Client(客户端)

创建命令对象并将其配置给调用者。

1.2. ✅ 举例类比(生活化):

点餐系统中:

  • 顾客 → Invoker
  • 服务员传菜 → Command
  • 厨师 → Receiver
  • 每道菜 → ConcreteCommand

2. 命令设计模式结构

命令模式包含如下角色:

  • Command: 抽象命令类
  • ConcreteCommand: 具体命令类
  • Invoker: 调用者
  • Receiver: 接收者
  • Client:客户类

2.1. 命令设计模式类图

2.2. 命令设计模式时序图

3. 命令设计模式实现方式

命令设计模式的实现方式,核心在于 将命令抽象成独立对象,使得发送者和接收者解耦。下面是标准实现结构和 Java 示例实现方式,结合注解风格以方便应用于 Spring 项目中。

3.1. 定义命令接口(Command)

public interface Command {
    void execute();
}

3.2. 创建接收者类(Receiver)

public class RiskDecisionReceiver {
    public void approve() {
        System.out.println("审批通过逻辑执行...");
    }

    public void reject() {
        System.out.println("审批拒绝逻辑执行...");
    }
}

3.3. 创建具体命令类(ConcreteCommand)

public class ApproveCommand implements Command {
    
    private final RiskDecisionReceiver receiver;

    public ApproveCommand(RiskDecisionReceiver receiver) {
        this.receiver = receiver;
    }

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

public class RejectCommand implements Command {
    
    private final RiskDecisionReceiver receiver;

    public RejectCommand(RiskDecisionReceiver receiver) {
        this.receiver = receiver;
    }

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

3.4. 调用者类(Invoker)

public class DecisionInvoker {
    
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void run() {
        command.execute();
    }
}

3.5. 客户端调用示例(Client)

public class RiskDecisionClient {
    
    public static void main(String[] args) {
        RiskDecisionReceiver receiver = new RiskDecisionReceiver();
        Command approveCommand = new ApproveCommand(receiver);
        Command rejectCommand = new RejectCommand(receiver);

        DecisionInvoker invoker = new DecisionInvoker();

        // 审批通过流程
        invoker.setCommand(approveCommand);
        invoker.run();

        // 审批拒绝流程
        invoker.setCommand(rejectCommand);
        invoker.run();
    }
}

3.6. Spring 实现提示(可选)

如果你使用 Spring,可以用如下方式注入和管理:

  • 使用 @Component 管理 ReceiverConcreteCommand
  • 使用 @Autowired 注入 Receiver 到命令中
  • 用策略注册表 + 命令名映射管理命令

结果好处

  • 增加新命令非常容易(只需实现接口)
  • 接收者逻辑和命令发送者解耦
  • 可以实现撤销、重做、命令日志等增强功能

4. 命令设计模式适合场景

4.1. ✅ 适合使用命令设计模式的场景

场景

说明

需要将请求调用者与接收者解耦

调用者只负责触发命令,不知道命令如何实现。

需要对请求排队、记录日志、支持撤销/重做

如编辑器中的操作撤销、银行交易日志等。

需要支持宏命令(多个命令组合执行)

比如批量操作、流水线处理等。

需要将操作封装为对象传递

方便将命令存入队列、数据库,延迟执行。

系统中存在多个操作行为,需要灵活扩展

每种行为封装为命令,新增行为无需改调用者逻辑。

远程调用 / 宏任务调度

比如分布式系统中对远程服务的封装。

4.2. ❌ 不适合使用命令设计模式的场景

场景

原因

业务操作非常简单,没有扩展需求

引入命令对象反而让结构复杂,得不偿失。

命令对象数量极多且变化频繁

会导致命令类泛滥,维护成本变高。

不需要记录历史或撤销操作的业务

命令封装就失去了很多优势,增加冗余。

操作同步且不需要解耦调用者和执行者

可以直接调用接收者方法,无需封装为命令。

高性能敏感系统中

引入命令对象和调用链会带来额外性能开销。

4.3. 📌 命令设计模式的场景总结:

项目

命令模式适用

不适用

是否需要行为解耦

✅ 是

❌ 否

是否需要日志/撤销/排队

✅ 是

❌ 否

行为是否变化频繁

✅ 是

❌ 否(简单稳定)

是否存在命令组合需求

✅ 是

❌ 否

是否为高频调用或性能敏感

❌ 否(不适用)

✅ 是

5. 命令设计模式实战示例

5.1. 🌟 场景简介

风控命令中心根据业务场景动态执行不同的风控策略,如:

  • 黑名单校验
  • 信用评分校验
  • 设备指纹校验等

5.2. 1️⃣ 命令接口定义

public interface RiskCommand {
    void execute(RiskContext context);
}

5.3. 2️⃣ 风控上下文对象(命令输入参数)

public class RiskContext {
    private String userId;
    private String ip;
    private String deviceId;
    private Map<String, Object> resultMap = new HashMap<>();

    // getter/setter省略
}

5.4. 3️⃣ 各种具体命令实现

5.4.1. 黑名单校验命令

@Component("blacklistCommand")
public class BlacklistCommand implements RiskCommand {

    @Override
    public void execute(RiskContext context) {
        // 模拟黑名单校验逻辑
        System.out.println("执行黑名单校验 for user: " + context.getUserId());
        context.getResultMap().put("blacklist", false);
    }
}

5.4.2. 信用分数校验命令

@Component("creditScoreCommand")
public class CreditScoreCommand implements RiskCommand {

    @Override
    public void execute(RiskContext context) {
        // 模拟信用分校验逻辑
        System.out.println("执行信用分校验 for user: " + context.getUserId());
        context.getResultMap().put("creditScorePass", true);
    }
}

5.5. 4️⃣ 命令调用者(Invoker)

@Component
public class RiskCommandInvoker {

    @Autowired
    private ApplicationContext applicationContext;

    public void runCommands(List<String> commandNames, RiskContext context) {
        for (String name : commandNames) {
            RiskCommand command = applicationContext.getBean(name, RiskCommand.class);
            command.execute(context);
        }
    }
}

5.6. 5️⃣ 控制器或服务调用示例

@Service
public class RiskEngineService {

    @Autowired
    private RiskCommandInvoker invoker;

    public void runRiskProcess(String userId) {
        RiskContext context = new RiskContext();
        context.setUserId(userId);
        context.setIp("192.168.1.1");
        context.setDeviceId("DEVICE123");

        List<String> commands = Arrays.asList("blacklistCommand", "creditScoreCommand");
        invoker.runCommands(commands, context);

        System.out.println("风控结果: " + context.getResultMap());
    }
}

5.7. ✅ 总结优势

说明

解耦

控制器无需知道风控规则细节

灵活扩展

添加新命令只需实现接口并加 @Component

动态组合

命令名可来自配置文件、数据库、用户输入等

日志/回滚

每个命令可独立记录日志或支持回滚逻辑

6. 命令设计模式思考

6.1. 命令模式是不是适合用于在Controller 层与service层之间?用于构建Controller 的命令?

6.1.1. 可以使用,但不是最佳常规选择

命令模式主要用于封装请求为对象,从而实现请求的参数化、请求排队、撤销、日志、事务等操作。在某些特定场景下可以作为Controller与Service之间的中间层抽象使用,例如:

6.1.2. ✅ 适用场景

场景

说明

控制层处理的业务请求种类多、变化大

使用命令模式封装请求参数及处理逻辑,使代码结构清晰

控制层希望将不同业务处理模块“延迟执行”或“排队执行”

命令可异步队列执行

需要记录请求日志、支持撤销/回滚等扩展行为

命令对象天然适合这些行为

多个 Controller 共用相似的业务指令

可将通用处理抽象成命令复用

例如在风控、工作流、审批流等系统,Controller 接收请求后会根据参数生成命令对象,并交由统一的命令执行器处理逻辑,非常合适使用命令模式。

6.1.3. ❌ 不适合场景(常规应用)

对于大部分业务系统,如果 Controller 只是简单地调用 Service 的方法,并没有复杂的行为拆解需求:

  • 命令模式反而会引入过多抽象
  • 增加类和代码复杂度
  • 开发维护成本上升

6.1.4. ✳️ 总结判断标准

需求

是否适合命令模式

请求结构稳定、简单

❌ 不适合

业务逻辑复杂、执行方式灵活

✅ 适合

希望请求可记录、排队、组合、延迟执行

✅ 非常适合

控制层职责只做参数接收和转发

❌ 不适合

6.1.5. ✅ 示例(适合的 Controller 场景)

@RestController
public class RiskController {

    @Autowired
    private RiskCommandInvoker invoker;

    @PostMapping("/risk/check")
    public ResponseEntity<String> riskCheck(@RequestBody RiskRequest request) {
        RiskContext context = new RiskContext();
        context.setUserId(request.getUserId());

        // 将 Controller 参数转化为一组命令
        List<String> commands = Arrays.asList("blacklistCommand", "creditScoreCommand");

        invoker.runCommands(commands, context);

        return ResponseEntity.ok("风控结果: " + context.getResultMap());
    }
}

博文参考

  • 1. 命令模式 — Graphic Design Patterns
  • 责任链设计模式(职责链模式)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

03 APP 自动化-定位元素工具元素定位

文章目录 一、Appium常用元素定位工具1、U IAutomator View Android SDK 自带的定位工具2、Appium Desktop Inspector3、Weditor安装&#xff1a;Weditor工具的使用 4、uiautodev通过定位工具获取app页面元素有哪些属性 二、app 元素定位方法 一、Appium常用元素定位工具 1、U…

PABD 2025:大数据与智慧城市管理的融合之道

会议简介 2025年公共管理与大数据国际会议&#xff08;ICPMBD 2025&#xff09;确实在海口举办。本次会议将围绕公共管理与大数据的深度融合、数据分析在公共管理中的应用、大数据驱动的政策制定与优化等议题展开深入研讨。参会者将有机会聆听前沿学术报告&#xff0c;分享研究…

Golang持续集成与自动化测试和部署

概述 Golang是一门性能优异的静态类型语言&#xff0c;但因其奇快的编译速度&#xff0c;结合DevOps, 使得它也非常适合快速开发和迭代。 本文讲述如何使用Golang, 进行持续集成与自动化测试和部署。主要使用了以下相关技术&#xff1a; dep&#xff1a; 进行包的依赖管理gin…

mysql离线安装教程

1.下载地址: https://downloads.mysql.com/archives/community/ 2.上传安装包到系统目录,并解压 tar -xvf mysql-8.0.34-1.el7.x86_64.rpm-bundle.tar3.检查系统中是否存在mariadb的rpm包 rpm -qa|grep mariadb存在则删除 rpm -e xxx4.解压完后执行如下命令安装 sudo rpm -iv…

基于FPGA的VGA显示文字和动态数字基础例程,进而动态显示数据,类似温湿度等

基于FPGA的VGA显示文字和数字 前言一、VGA显示参数二、字模生成三、代码分析1.vga_char顶层2.vga_ctrl驱动文件3.vga_pic数据准备文件 总结 前言 结合正点原子以及野火的基础例程&#xff0c;理解了VGA本身基本协议&#xff0c;VGA本身显示像素为640*480&#xff0c;因此注意生…

力扣刷题Day 68:搜索插入位置(35)

1.题目描述 2.思路 方法1&#xff1a;回溯的二分查找。 方法2&#xff1a;看到了一个佬很简洁的写法&#xff0c;代码贴在下面了。 3.代码&#xff08;Python3&#xff09; 方法1&#xff1a; class Solution:def searchInsert(self, nums: List[int], target: int) ->…

使用Python绘制节日祝福——以端午节和儿童节为例

端午节 端午节总算是回家了&#xff0c;感觉时间过得真快&#xff0c;马上就毕业了&#xff0c;用Python弄了一个端午节元素的界面&#xff0c;虽然有点不像&#xff0c;祝大家端午安康。端午节粽子&#xff08;python&#xff09;_python画粽子-CSDN博客https://blog.csdn.net…

C#项目07-二维数组的随机创建

实现需求 创建二维数组&#xff0c;数组的列和宽为随机&#xff0c;数组内的数也是随机 知识点 1、Random类 Public Random rd new Random(); int Num_Int rd.Next(1, 100);2、数组上下限。 //定义数组 int[] G_Array new int[1,2,3,4];//一维数组 int[,] G_Array_T …

光伏功率预测 | LSTM多变量单步光伏功率预测(Matlab完整源码和数据)

光伏功率预测 | MATLAB实现基于LSTM长短期记忆神经网络的光伏功率预测 目录 光伏功率预测 | MATLAB实现基于LSTM长短期记忆神经网络的光伏功率预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 光伏功率预测 | LSTM多变量单步光伏功率预测&#xff08;Matlab完整源码和…

一步一步配置 Ubuntu Server 的 NodeJS 服务器详细实录——3. 服务器软件更新,以及常用软件安装

前言 前面&#xff0c;我们已经 安装好了 Ubuntu 服务器系统&#xff0c;并且 配置好了 ssh 免密登录服务器 &#xff0c;现在&#xff0c;我们要来进一步的设置服务器。 那么&#xff0c;本文&#xff0c;就是进行服务器的系统更新&#xff0c;以及常用软件的安装 调整 Ubu…

PyTorch 入门学习笔记

一、简介 PyTorch 是由 Meta&#xff08;原 Facebook&#xff09; 开源的深度学习框架。其前身 Torch 是一个基于 LuaJIT 的科学计算框架&#xff0c;核心功能是提供高效的张量&#xff08;Tensor&#xff09;操作和神经网络支持。由于 Lua 语言的生态限制&#xff0c;Torch 逐…

pycharm生成图片

文章目录 图片例子生成图片并储存&#xff0c;设置中文字体支持两条线绘制散点图和直方图绘制条形图&#xff08;bar&#xff09;绘制条形图&#xff08;横着的&#xff09;&#xff08;plt.barh&#xff09;分组的条形图 颜色和线条风格1. **颜色字符 (color)**其他颜色指定方…

Android 云手机横屏模式下真机键盘遮挡输入框问题处理

一、背景 打开横屏应用,点击云机EditText输入框,输入框被键盘遮挡,如下图&#xff1a; 未打开键盘状态: 点击第二个输入框,键盘遮挡了输入框&#xff1a; 二、解决方案&#xff08;推荐第三中方案,博主采用的也是第三种方案&#xff09; 博主这里整理了三种方案&#xff1a;…

Axure设计案例——科技感对比柱状图

想让数据对比展示摆脱平淡无奇&#xff0c;瞬间抓住观众的眼球吗&#xff1f;那就来看看这个Axure设计的科技感对比柱状图案例&#xff01;科技感设计风格运用独特元素打破传统对比柱状图的常规&#xff0c;营造出一种极具冲击力的视觉氛围。每一组柱状体都仿佛是科技战场上的士…

FPGA仿真中阻塞赋值(=)和非阻塞赋值(<=)区别

FPGA仿真中阻塞赋值和非阻塞赋值的区别 单独仿真小模块对但将小模块加入整个工程仿真不对就有可能是没有注意到仿真中阻塞赋值和非阻塞赋值的区别 目录 前言 一、简介 二、设计实例 三、仿真实例 1、仿真用非阻塞赋值 2、仿真用阻塞赋值 总结 前言 网上很多人介绍verilo…

LabVIEW轴角编码器自动检测

LabVIEW 开发轴角编码器自动检测系统&#xff0c;针对指控系统中高故障率的轴角编码器性能检测需求&#xff0c;通过模块化硬件架构与软件设计&#xff0c;实现编码器运转状态模拟、扭矩 / 转速实时监测、19 位并行编码采集译码、数据自动分析及报告生成等功能&#xff0c;解决…

MySQL数据库从0到1

目录 数据库概述 基本命令 查询命令 函数 表的操作 增删改数据和表结构 约束 事务 索引 视图 触发器 存储过程和函数 三范式 数据库概述 SQL语句的分类&#xff1a; DQL&#xff1a;查询语句&#xff0c;凡是select语句都是DQL。 DML&#xff1a;insert,delete,up…

WiFi万能钥匙鲲鹏服务器部署 TiDB 集群实战指南

作者&#xff1a; TiDBer_yangxi 原文来源&#xff1a; https://tidb.net/blog/15a234d0 一、环境准备 1. 硬件要求 服务器架构 &#xff1a;鲲鹏服务器&#xff08;ARM架构&#xff09;&#xff0c;TiDB 官方明确支持 ARM 架构服务器部署 推荐配置 &#xff08;生产环…

Vue 核心技术与实战智慧商城项目Day08-10

温馨提示&#xff1a;这个黑马的视频在b占可以找到&#xff0c;里面有完整的教学过程 然后这个项目有完整的代码&#xff0c;我已经上传了&#xff0c;如果审核成功大家就可以看了&#xff0c;但是需要审核多久我也不是很确定 1.项目演示 2. 项目收获 3. 创建项目 4. 调整初始化…

TCP/IP协议精华总结pdf分享

hi &#xff0c;大家好&#xff0c;应小伙伴们的要求&#xff0c;上次分享了个人的一些学习和职场经验&#xff0c;其中网络协议PDF文档是我之前学习协议的时候总结一些精华知识&#xff0c;网络属于基本功&#xff0c;是互联网必备知识&#xff0c;我深信掌握好核心20%知识&am…