【设计模式】【行为型模式】命令模式(Command)

news2025/2/13 3:48:52

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中… 博客更新速度++
📫 欢迎+V: flzjcsg2,我们共同讨论Java深渊的奥秘
🎵 当你的天空突然下了大雨,那是我在为你炸乌云

文章目录

  • 一、入门
    • 什么是命令模式?
    • 为什么需要命令模式?
    • 怎样实现命令模式?
  • 二、命令模式在源码中的运用
    • 2.1、JDK的Runnable接口
    • 2.2、Spring 的 CommandLineRunner 接口
  • 三、总结
    • 命令模式的优点
    • 命令模式的缺点
    • 命令模式的使用场景
  • 参考

一、入门

什么是命令模式?

命令模式是一种行为设计模式,它将请求或操作封装为对象,从而使你可以用不同的请求对客户进行参数化,并支持请求的排队、记录、撤销等操作。
命令模式的核心是将“请求”封装为独立的对象,包含执行操作所需的所有信息。这样,你可以将请求与执行者解耦,并通过参数化、队列或日志等方式管理请求。

为什么需要命令模式?

在没有使用命令模式的情况下,代码可能会遇到以下问题:

  • 紧耦合
    • 调用者(Invoker)直接依赖接收者(Receiver)的具体实现。如果接收者的接口或行为发生变化,调用者也需要修改。
    • 例如,一个按钮直接调用某个对象的特定方法,导致按钮代码与具体逻辑紧密耦合。
  • 难以扩展
    • 如果需要添加新的操作,必须修改调用者的代码,违反了开闭原则(对扩展开放,对修改关闭)。
    • 例如,一个遥控器需要支持新的设备时,必须修改遥控器的代码。
  • 不支持撤销、重做或事务操作
    • 如果系统需要支持撤销、重做或事务操作,直接调用方法的方式难以实现这些功能。
    • 例如,一个文本编辑器需要支持撤销操作,直接调用方法的方式无法记录历史状态。
  • 难以实现请求的队列或日志
    • 如果需要对请求进行排队、延迟执行或记录日志,直接调用方法的方式无法实现这些功能。

怎样实现命令模式?

命令模式的组成:
命令(Command):定义执行操作的接口,通常包含一个 execute() 方法。
具体命令(Concrete Command):实现命令接口,负责调用接收者的操作。
接收者(Receiver):实际执行操作的对象。
调用者(Invoker):持有命令对象,并触发命令的执行。
客户端(Client):创建命令对象并设置其接收者。

【案例】 开关灯
在这里插入图片描述
Light(接收者):实际执行操作的对象。包含 on()off()方法。’

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

    public void off() {
        System.out.println("Light is OFF");
    }
}

Command(命令接口):定义执行操作的接口,包含 execute()方法。

interface Command {
    void execute();
}

LightOnCommandLightOffCommand(具体命令):实现 Command 接口,封装了对 Light 的操作。持有 Light 对象的引用,并在 execute() 方法中调用 Light 的方法。

// 具体命令:开灯
class LightOnCommand implements Command {
    private Light light;

    public LightOnCommand(Light light) {
        this.light = light;
    }

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

// 具体命令:关灯
class LightOffCommand implements Command {
    private Light light;

    public LightOffCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.off();
    }
}

RemoteControl(调用者):持有 Command 对象的引用。通过pressButton()方法触发命令的执行。

class RemoteControl {
    private Command command;

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

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

CommandPatternDemo(客户端):创建接收者、命令对象和调用者,并将它们组装在一起。

// 客户端
public class CommandPatternDemo {
    public static void main(String[] args) {
        // 创建接收者
        Light light = new Light();

        // 创建命令对象
        Command lightOn = new LightOnCommand(light);
        Command lightOff = new LightOffCommand(light);

        // 创建调用者
        RemoteControl remote = new RemoteControl();

        // 执行开灯命令
        remote.setCommand(lightOn);
        remote.pressButton();

        // 执行关灯命令
        remote.setCommand(lightOff);
        remote.pressButton();
    }
}

二、命令模式在源码中的运用

2.1、JDK的Runnable接口

Java 中的 Runnable 接口是命令模式的一个典型例子。
命令接口(类似于 Command 接口):Runnable接口。

public interface Runnable {
    void run();
}

具体命令(类似于 ConcreteCommand):我们自己实现的task,MyTask

public class MyTask implements Runnable {

	// 在这里可以加接收这
	
    @Override
    public void run() {
        System.out.println("Task is running");
    }
}

调用者(类似于 Invoker): Thead类,下面是简化版

public class Thread {
    private Runnable target;

    public Thread(Runnable target) {
        this.target = target;
    }

    public void start() {
        task.run();
    }
}

客户端

public class Main {
    public static void main(String[] args) {
        Runnable task = new MyTask(); // 创建具体命令
        Thread thread = new Thread(task); // 设置命令
        thread.start(); // 执行命令
    }
}

2.2、Spring 的 CommandLineRunner 接口

CommandLineRunner 是 Spring 框架中一个非常有用的接口,通常用于在 Spring Boot 应用启动后执行一些初始化任务或自定义逻辑。它本质上是命令模式的一个典型应用,将“启动时需要执行的任务”封装为一个命令对象,并由 Spring Boot 在合适的时机统一执行。
CommandLineRunner 的作用:CommandLineRunner 接口的主要作用是在 Spring Boot 应用启动完成后,执行一些额外的逻辑。例如:初始化数据、加载配置文件、启动后台任务、执行一些检查或测试逻辑。
命令接口(类似于 Command 接口)CommandLineRunner

@FunctionalInterface
public interface CommandLineRunner {
    void run(String... args) throws Exception;
}

具体命令(类似于 ConcreteCommand)MyStartupTask

@Component // 将类注册为 Spring Bean
public class MyStartupTask implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        System.out.println("Executing startup task...");

        // 打印命令行参数
        System.out.println("Command line arguments:");
        for (String arg : args) {
            System.out.println(arg);
        }

        // 执行自定义逻辑
        initializeDatabase();
        loadConfiguration();
    }

    private void initializeDatabase() {
        System.out.println("Initializing database...");
        // 初始化数据库的逻辑
    }

    private void loadConfiguration() {
        System.out.println("Loading configuration...");
        // 加载配置文件的逻辑
    }
}

调用者(类似于 Invoker): SpringApplication,下面的代码是简化版

public class SpringApplication {
    public void run(String... args) {
        // 初始化 Spring 上下文
        ConfigurableApplicationContext context = createApplicationContext();
        refreshContext(context);

        // 调用 CommandLineRunner
        callRunners(context, args);
    }

    private void callRunners(ApplicationContext context, String[] args) {
        // 获取所有 CommandLineRunner 的 Bean
        Map<String, CommandLineRunner> runners = context.getBeansOfType(CommandLineRunner.class);

        // 按顺序执行
        List<CommandLineRunner> sortedRunners = new ArrayList<>(runners.values());
        AnnotationAwareOrderComparator.sort(sortedRunners);

        // 调用每个 CommandLineRunner 的 run() 方法
        for (CommandLineRunner runner : sortedRunners) {
            runner.run(args);
        }
    }
}

客户端

@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

三、总结

命令模式的优点

  • 解耦调用者和接收者:
    • 调用者(Invoker)不需要知道具体的接收者(Receiver)是谁,只需要调用命令对象的 execute() 方法。
    • 降低了系统的耦合度,使得调用者和接收者可以独立变化。
  • 支持扩展:
    • 可以轻松添加新的命令类,而不需要修改调用者的代码。
    • 符合开闭原则(对扩展开放,对修改关闭)。
  • 支持撤销和重做:
    • 命令对象可以记录状态,从而支持撤销(undo)和重做(redo)操作。
    • 例如,文本编辑器可以通过命令对象记录每次操作的状态,从而实现撤销功能。
  • 支持请求的队列或日志:
    • 命令对象可以被排队、延迟执行或记录日志。
    • 例如,可以将命令对象放入队列中,按顺序执行,或者将命令对象记录到日志中以便后续重放。
  • 支持事务操作:
    • 可以将多个命令组合成一个复合命令,实现事务操作。
    • 例如,在数据库操作中,可以将多个更新操作封装为一个事务。

命令模式的缺点

  • 类的数量增加:
    • 每个命令都需要一个具体的类,可能导致类的数量增多。
    • 对于简单的操作,使用命令模式可能会显得过于繁琐。
  • 复杂性增加:
    • 对于简单的请求,直接调用方法可能更直观,使用命令模式会增加额外的复杂性。
    • 需要额外的代码来管理命令对象(如队列、日志等)。

命令模式的使用场景

  • 需要解耦调用者和接收者:
    • 当调用者不需要知道接收者的具体实现时,可以使用命令模式。
    • 例如,GUI 中的按钮点击事件、远程调用的请求处理等。
  • 需要支持撤销、重做或事务操作:
    • 当系统需要支持撤销、重做或事务操作时,命令模式是一个很好的选择。
    • 例如,文本编辑器、绘图软件、数据库事务等。
  • 需要将请求排队或记录日志:
    • 当需要对请求进行排队、延迟执行或记录日志时,可以使用命令模式。
    • 例如,任务调度系统、消息队列、操作日志等。
  • 需要支持扩展:
    • 当系统需要支持新的操作,而不希望修改现有代码时,可以使用命令模式。
    • 例如,遥控器支持新的设备、插件系统等。

参考

黑马程序员Java设计模式详解, 23种Java设计模式(图解+框架源码分析+实战)_哔哩哔哩_bilibili

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

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

相关文章

C++模拟实现AVL树

目录 1.文章概括 2.AVL树概念 3.AVL树的性质 4.AVL树的插入 5.旋转控制 1.左单旋 2. 右单旋 3.左右双旋 4.右左双旋 6.全部代码 1.文章概括 本文适合理解平衡二叉树的读者阅读&#xff0c;因为AVL树是平衡二叉树的一种优化&#xff0c;其大部分实现逻辑与平衡二叉树是…

python卷积神经网络人脸识别示例实现详解

目录 一、准备 1&#xff09;使用pytorch 2&#xff09;安装pytorch 3&#xff09;准备训练和测试资源 二、卷积神经网络的基本结构 三、代码实现 1&#xff09;导入库 2&#xff09;数据预处理 3&#xff09;加载数据 4&#xff09;构建一个卷积神经网络 5&#xff0…

以Unity6.0为例,如何在Unity中开启DLSS功能

DLSS DLSS&#xff08;NVIDIA 深度学习超级采样&#xff09;&#xff1a;NVIDIA DLSS 是一套由 GeForce RTX™ Tensor Core 提供支持的神经渲染技术&#xff0c;可提高帧率&#xff0c;同时提供可与原生分辨率相媲美的清晰、高质量图像。目前最新突破DLSS 4 带来了新的多帧…

CSDN 大模型 笔记

AI 3大范式&#xff1a;计算 发发 交互 L1 生成代码 复制到IDEA &#xff08;22年12-23年6&#xff0c;7月份&#xff09; L2 部分自动编程 定义class 设计interface 让其填充实现 (23年7&#xff0c;8月份) L3 通用任务 CRUD (24年) L4 高度自动编程 通用领域专有任务&#xf…

Stability AI 联合 UIUC 提出单视图 3D 重建方法SPAR3D,可0.7秒完成重建并支持交互式用户编辑。

Stability AI 联合 UIUC 提出一种简单而有效的单视图 3D 重建方法 SPAR3D&#xff0c;这是一款最先进的 3D 重建器&#xff0c;可以从单视图图像重建高质量的 3D 网格。SPAR3D 的重建速度很快&#xff0c;只需 0.7 秒&#xff0c;并支持交互式用户编辑。 相关链接 论文&#xf…

网易易盾接入DeepSeek,数字内容安全“智”理能力全面升级

今年农历新年期间&#xff0c;全球AI领域再度掀起了一波革命性浪潮&#xff0c;国产通用大模型DeepSeek凭借其强大的多场景理解与内容生成能力迅速“出圈”&#xff0c;彻底改写全球人工智能产业的格局。 作为国内领先的数字内容风控服务商&#xff0c;网易易盾一直致力于探索…

自动驾驶---如何打造一款属于自己的自动驾驶系统

在笔者的专栏《自动驾驶Planning决策规划》中&#xff0c;主要讲解了行车的相关知识&#xff0c;从Routing&#xff0c;到Behavior Planning&#xff0c;再到Motion Planning&#xff0c;以及最后的Control&#xff0c;笔者都做了相关介绍&#xff0c;其中主要包括算法在量产上…

聚焦 AUTO TECH China 2025,共探汽车内外饰新未来Automotive Interiors

全球汽车产业蓬勃发展的大背景下&#xff0c;汽车内外饰作为汽车重要组成部分&#xff0c;其市场需求与技术创新不断推动着行业变革。2025年11月20日至22日&#xff0c;一场备受瞩目的行业盛会 ——AUTO TECH China 2025 广州国际汽车内外饰技术展览会将在广州保利世贸博览馆盛…

Moretl 增量文件采集工具

永久免费: <下载> <使用说明> 用途 定时全量或增量采集工控机,电脑文件或日志. 优势 开箱即用: 解压直接运行.不需额外下载.管理设备: 后台统一管理客户端.无人值守: 客户端自启动,自更新.稳定安全: 架构简单,兼容性好,通过授权控制访问. 架构 技术架构: Asp…

支持多种网络数据库格式的自动化转换工具——VisualXML

一、VisualXML软件介绍 对于DBC、ARXML……文件的编辑、修改等繁琐操作&#xff0c;WINDHILL风丘科技开发的总线设计工具——VisualXML&#xff0c;可轻松解决这一问题&#xff0c;提升工作效率。 VisualXML是一个强大且基于Excel表格生成多种网络数据库文件的转换工具&#…

四、OSG学习笔记-基础图元

前一章节&#xff1a; 三、OSG学习笔记-应用基础-CSDN博客https://blog.csdn.net/weixin_36323170/article/details/145514021 代码&#xff1a;CuiQingCheng/OsgStudy - Gitee.com 一、绘制盒子模型 下面一个简单的 demo #include<windows.h> #include<osg/Node&…

window 安装GitLab服务器笔记

目录 视频&#xff1a; 资源&#xff1a; Linux CeneOS7&#xff1a; VMware&#xff1a; Linux无法安装 yum install vim -y 1.手动创建目录 2.下载repo PS 补充视频不可复制的代码 安装GitLab *修改root用户密码相关&#xff08;我卡在第一步就直接放弃了这个操作&…

MySQL数据库入门到大蛇尚硅谷宋红康老师笔记 基础篇 part 10

第10章_创建和管理表 DDL&#xff1a;数据定义语言。CREATE \ALTER\ DROP \RENAME TRUNCATE DML&#xff1a;数据操作语言。INSERT \DELETE \UPDATE \SELECT&#xff08;重中之重&#xff09; DCL&#xff1a;数据控制语言。COMMIT \…

前端如何判断浏览器 AdBlock/AdBlock Plus(最新版)广告屏蔽插件已开启拦截

2个月前AdBlock/AdBlock Plus疑似升级了一次 因为自己主要负责面对海外的用户项目&#xff0c;发现以前的检测AdBlock/AdBlock Plus开启状态方法已失效了&#xff0c;于是专门研究了一下。并尝试了很多方法。 已失效的老方法 // 定义一个检测 AdBlock 的函数 function chec…

html文件怎么转换成pdf文件,2025最新教程

将HTML文件转换成PDF文件&#xff0c;可以采取以下几种方法&#xff1a; 一、使用浏览器内置功能 打开HTML文件&#xff1a;在Chrome、Firefox、IE等浏览器中打开需要转换的HTML文件。打印对话框&#xff1a;按下CtrlP&#xff08;Windows&#xff09;或CommandP&#xff08;M…

科技查新过不了怎么办

“科技查新过不了怎么办&#xff1f;” “科技查新不通过的原因是什么&#xff1f;” 想必这些问题一直困扰着各位科研和学术的朋友们&#xff0c;尤其是对于查新经验不够多的小伙伴&#xff0c;在历经千难万险&#xff0c;从选择查新机构、填写线上委托单到付费&#xff0c;…

超详细的数据结构3(初阶C语言版)栈和队列。

文章目录 栈和队列1.栈1.1 概念与结构1.2 栈的实现 2. 队列2.1 概念与结构2.2 队列的实现 总结 栈和队列 1.栈 1.1 概念与结构 栈&#xff1a;⼀种特殊的线性表&#xff0c;其只允许在固定的⼀端进行插⼊和删除元素操作。进⾏数据插⼊和删除操作的⼀端称为栈顶&#xff0c;另…

centos 7 关于引用stdatomic.h的问题

问题&#xff1a;/tmp/tmp4usxmdso/main.c:6:23: fatal error: stdatomic.h: No such file or directory #include <stdatomic.h> 解决步骤&#xff1a; 1.这个错误是因为缺少C编译器的标准原子操作头文件 stdatomic.h。在Linux系统中&#xff0c;我们需要安装开发工具…

Unity WebGL包体压缩

最近在开发webgl&#xff0c;踩了很多坑&#xff0c;先来说下包体的问题。 开发完之后发现unity将文件都合并到一个文件了&#xff0c;一共有接近100m。 这对网页端的体验来说是可怕的&#xff0c;因为玩家必须要加载完所有的文件才能进入&#xff0c;这样体验特别差。 于是想…

【对比测评】 .NET 应用的 Web 视图控件:DotNetBrowser 或 EO.WebBrowser

您是否需要 .NET 应用的 Web 视图控件&#xff1f;.NET 生态系统提供了很多东西&#xff0c;有免费的 Web 视图控件&#xff0c;既有开源的&#xff0c;也有专有的。还有一些商业 Web 视图 控件&#xff0c;也是企业经常选择的一种选项。 在这篇博文中&#xff0c;我们比较了商…