当前位置: 首页 > news >正文

Java 设计模式・命令模式篇:从思想到代码实现

一、行为型模式

在面向对象的世界里,如何优雅地组织对象间的交互、分配职责,是每一位开发者都会反复思考的问题。直接硬编码交互逻辑固然简单,但当业务复杂度上升、对象协作关系变得错综复杂时,这种方式就会让代码变得僵化、难以扩展。

行为型设计模式正是为了解决这一痛点而诞生的一套思想体系。它们关注如何定义对象之间的通信方式和职责分配,通过命令、迭代、观察者、策略等手段,让对象间的协作更具灵活性、可复用性和可维护性。

在 Java 开发中,行为型模式主要包含以下 11 种经典实现:

  1. 模板方法模式 (Template Method):定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。Java 设计模式・模板方法模式篇:从思想到代码实现-CSDN博客
  2. 策略模式 (Strategy):定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换,让算法独立于使用它的客户而变化。Java 设计模式・策略模式篇:从思想到代码实现-CSDN博客
  3. 命令模式 (Command):将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,支持可撤销操作。
  4. 责任链模式 (Chain of Responsibility):将请求的发送者和接收者解耦,使多个对象都有机会处理这个请求,形成一条处理链。Java 设计模式・责任链模式篇:从思想到代码实现-CSDN博客
  5. 状态模式 (State):允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。Java 设计模式・状态模式篇:从思想到代码实现-CSDN博客
  6. 观察者模式 (Observer):定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并被自动更新。Java 设计模式・观察者模式篇:从思想到代码实现-CSDN博客
  7. 中介者模式 (Mediator):用一个中介对象来封装一系列的对象交互,使各对象不需要显式地相互引用,从而降低耦合。Java 设计模式・中介者模式篇:从思想到代码实现-CSDN博客
  8. 迭代器模式 (Iterator):提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。Java 设计模式・迭代器模式篇:从思想到代码实现-CSDN博客
  9. 访问者模式 (Visitor):表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。Java 设计模式・访问者模式篇:从思想到代码实现-CSDN博客
  10. 备忘录模式 (Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后恢复。Java 设计模式・备忘录模式篇:从思想到代码实现-CSDN博客
  11. 解释器模式 (Interpreter):给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。Java 设计模式・解释器模式篇:从思想到代码实现-CSDN博客

二、命令模式

2.1 介绍

命令模式(Command Pattern)是一种行为型设计模式,它的核心定义是:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作

2.2 角色

  • 抽象命令类(Command)角色: 定义命令的接口,声明执行的方法。
  • 具体命令(Concrete Command)角色:具体的命令,实现命令接口;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
  • 实现者/接收者(Receiver)角色: 接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
  • 调用者/请求者(Invoker)角色: 要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。

三、代码实现

为了方便理解本文采用中文定义类名

3.1 接收者

public class 厨师 { // 做鱼香肉丝 public void cookYuXiangRouSi() { System.out.println("厨师:鱼香肉丝制作完成!"); } // 做宫保鸡丁 public void cookGongBaoJiDing() { System.out.println("厨师:宫保鸡丁制作完成!"); } }

3.2 抽象命令

public abstract class 点餐 { // 执行命令(做菜) abstract void execute(); }

3.3 具体命令

public class 点餐宫保鸡丁 extends 点餐{ // 持有接收者(厨师)的引用 private 厨师 chef; public 点餐宫保鸡丁(厨师 chef) { this.chef = chef; } @Override void execute() { chef.cookGongBaoJiDing(); } }
public class 点餐鱼香肉丝 extends 点餐{ // 持有接收者(厨师)的引用 private 厨师 chef; public 点餐鱼香肉丝(厨师 chef) { this.chef = chef; } @Override void execute() { chef.cookYuXiangRouSi(); } }

3.4 调用者

public class 服务员 { private 点餐 order; public void takeOrder(点餐 order) { this.order = order; System.out.println("服务员:已记录当前菜品!"); order.execute(); } }

3.5 客户端

public class 客户 { public static void main(String[] args) { 厨师 chef = new 厨师(); 点餐 order1 = new 点餐宫保鸡丁(chef); 点餐 order2 = new 点餐鱼香肉丝(chef); 服务员 waiter = new 服务员(); waiter.takeOrder(order1); waiter.takeOrder(order2); } }
服务员:已记录当前菜品! 厨师:宫保鸡丁制作完成! 服务员:已记录当前菜品! 厨师:鱼香肉丝制作完成! Process finished with exit code 0

四、代码实现(加上撤销)

4.1 接收者

public class 厨师 { // 做鱼香肉丝 public void cookYuXiangRouSi() { System.out.println("厨师:鱼香肉丝制作完成!"); } // 做宫保鸡丁 public void cookGongBaoJiDing() { System.out.println("厨师:宫保鸡丁制作完成!"); } // 取消做菜(支持撤销操作) public void cancelCook(String dishName) { System.out.println("厨师:取消制作" + dishName + ",清理食材!"); } }

4.2 抽象命令

public abstract class 点餐 { // 执行命令(做菜) abstract void execute(); // 撤销命令(取消做菜) abstract void undo(); }

4.3 具体命令

public class 点餐宫保鸡丁 extends 点餐{ // 持有接收者(厨师)的引用 private 厨师 chef; public 点餐宫保鸡丁(厨师 chef) { this.chef = chef; } @Override void execute() { chef.cookGongBaoJiDing(); } @Override public void undo() { chef.cancelCook("宫保鸡丁"); } }
public class 点餐鱼香肉丝 extends 点餐{ // 持有接收者(厨师)的引用 private 厨师 chef; public 点餐鱼香肉丝(厨师 chef) { this.chef = chef; } @Override void execute() { chef.cookYuXiangRouSi(); } @Override public void undo() { chef.cancelCook("鱼香肉丝"); } }

4.4 调用者

public class 服务员 { private 点餐 order; public void takeOrder(点餐 order) { this.order = order; System.out.println("服务员:已记录当前菜品!"); order.execute(); } // 撤销订单(支持撤销操作) public void undoOrder(点餐 order) { order.undo(); } }

4.5 客户端

public class 客户 { public static void main(String[] args) { 厨师 chef = new 厨师(); 点餐 order1 = new 点餐宫保鸡丁(chef); 点餐 order2 = new 点餐鱼香肉丝(chef); 服务员 waiter = new 服务员(); waiter.takeOrder(order1); waiter.takeOrder(order2); waiter.undoOrder(order2); } }
服务员:已记录当前菜品! 厨师:宫保鸡丁制作完成! 服务员:已记录当前菜品! 厨师:鱼香肉丝制作完成! 厨师:取消制作鱼香肉丝,清理食材!

五、代码实现(加上队列)

5.1 接收者,抽象命令,具体命令不变

5.2 调用者

public class 服务员 { private List<点餐> orders = new ArrayList<>(); public void takeOrder(点餐 order) { orders.add(order); System.out.println("客户:点餐成功!"); } // 提交所有订单(触发命令执行) public void submitOrders() { System.out.println("服务员:开始提交所有订单给厨师..."); for (点餐 order : orders) { order.execute(); // 执行每一个做菜命令 } orders.clear(); // 提交后清空订单 } // 撤销最后一个订单(支持撤销操作) public void undoLastOrder() { if (!orders.isEmpty()) { 点餐 lastOrder = orders.remove(orders.size() - 1); System.out.println("服务员:撤销最后一个订单..."); lastOrder.undo(); } else { System.out.println("服务员:暂无可撤销的订单!"); } } }

5.3 客户端

public class 客户 { public static void main(String[] args) { 厨师 chef = new 厨师(); 点餐 order1 = new 点餐宫保鸡丁(chef); 点餐 order2 = new 点餐鱼香肉丝(chef); 服务员 waiter = new 服务员(); waiter.takeOrder(order1); waiter.takeOrder(order2); waiter.undoLastOrder(); waiter.submitOrders(); } }
客户:点餐成功! 客户:点餐成功! 服务员:撤销最后一个订单... 厨师:取消制作鱼香肉丝,清理食材! 服务员:开始提交所有订单给厨师... 厨师:宫保鸡丁制作完成! Process finished with exit code 0

六、优缺点

6.1 优点

  • 彻底解耦调用者和接收者 调用者(如餐厅服务员)只需要调用命令的execute()方法,完全不用知道接收者(厨师)是谁、具体怎么执行(做菜步骤);接收者也无需关心谁发起了请求。

  • 极易扩展新命令 新增一个命令(如新增 “麻婆豆腐” 菜品),只需新增一个具体命令类,无需修改原有代码,完全符合 “开闭原则”。

  • 天然支持命令的撤销 / 重做、排队、日志记录 命令对象封装了执行的全部信息(做什么、谁来做),因此可以轻松实现

  • 可灵活组合命令(宏命令)可以将多个命令组合成一个 “宏命令”(复合命令),一次执行多个操作。

6.2 缺点

  • 类数量膨胀,增加系统复杂度 每一个具体的请求(如每一道菜品、遥控器的每一个按键功能)都需要对应一个具体命令类,当系统中有大量命令时,类的数量会急剧增加。
  • 简单场景下显得 “过度设计” 如果系统只有少量固定的命令,且不需要撤销、排队、日志等功能,使用命令模式会增加不必要的层级(调用者→命令→接收者),反而让代码更复杂。

  • 命令结果难以直接返回 命令模式的execute()方法通常是void类型(无返回值),如果需要获取接收者的执行结果(如厨师做菜耗时、订单金额),需要额外设计(如给命令对象加结果属性),增加了代码复杂度。

七、适用场景

7.1 适用

  • 需要解耦调用者和接收者的场景
  • 需要支持撤销 / 重做的场景
  • 需要对请求进行排队 / 日志记录 / 事务处理的场景
  • 需要批量执行多个操作的场景

7.2 不适合

  • 系统只有少量固定的命令,且不需要撤销、排队等扩展功能;
  • 对性能要求极高的场景(命令模式多一层封装,会有轻微的性能开销);
  • 简单的业务逻辑,直接调用方法比封装命令更简洁的场景。

八、对比学习

8.1 策略模式对比

Java 设计模式・策略模式篇:从思想到代码实现-CSDN博客

两者都属于行为型模式,且都有 “封装不同逻辑” 的特征,但核心目标完全不同。

维度命令模式策略模式
核心目标封装请求 / 操作(“做什么”)封装算法 / 策略(“怎么做”)
角色关系调用者→命令→接收者(三层解耦)环境类→策略(两层解耦)
核心能力支持撤销、排队、日志(管理请求)算法可替换(优化执行逻辑)
典型场景点餐、遥控器、撤销操作支付方式、排序算法、折扣计算

8.2 装饰器模式对比

Java 设计模式・装饰器模式篇:从思想到代码实现-CSDN博客

两者都属于 “封装”,但装饰器是 “增强功能”,命令模式是 “封装操作”,属于不同类型(装饰器是结构型,命令是行为型)。

维度命令模式装饰器模式
模式类型行为型(关注操作的执行 / 管理)结构型(关注对象功能的增强)
核心目标封装请求,解耦调用者和接收者动态给对象添加功能(不改变原有逻辑)
实现方式命令实现统一接口,绑定接收者装饰器包裹原对象,实现相同接口
典型场景点餐、遥控器给咖啡加奶、加糖;给日志加时间戳

8.3 模板方法模式对比

Java 设计模式・模板方法模式篇:从思想到代码实现-CSDN博客

两者都涉及 “操作的执行”,但核心是 “灵活替换操作” vs “固定流程 + 局部定制”。

维度命令模式模板方法模式
核心逻辑封装独立的、可替换的单个操作固定整体流程,只定制局部步骤(如 “做套餐”:固定流程 = 备料→烹饪→装盘,仅 “烹饪” 步骤可定制)
灵活性操作可完全替换(点鱼香肉丝 / 宫保鸡丁)流程固定,仅局部步骤可改(套餐流程不变,只换菜品做法)
角色关系调用者→命令→接收者(三层解耦)父类定流程,子类重写步骤(继承关系)
典型场景点餐、遥控器框架流程(如 Spring 初始化)、标准化流程(如考试:审题→答题→交卷)

九、源码举例 Runnable

9.1 角色对应

命令模式角色JDK 中的实现
抽象命令(Command)Runnable接口(run()方法)/Callable接口(call()方法)
具体命令(ConcreteCommand)自定义的Runnable实现类(如new Runnable() { ... }
调用者(Invoker)ThreadPoolExecutor(线程池)、Thread
接收者(Receiver)自定义run()方法里的业务逻辑(如打印、计算、IO 操作)

9.2 抽象命令

@FunctionalInterface public interface Runnable { public abstract void run(); }

9.2 调用者

public class Thread implements Runnable { ... private Runnable target; ... public synchronized void start() { if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { } } } private native void start0(); @Override public void run() { if (target != null) { target.run(); } } ... }

十、其它设计模式

Java 设计模式・总结目录篇:从思想到代码实现-CSDN博客

http://www.cnnetsun.cn/news/1669794.html

相关文章:

  • 用快马平台十分钟搭建小龙虾在线商城原型,验证你的餐饮电商创意
  • Cyber Engine Tweaks终极指南:3步掌握赛博朋克2077模组开发与优化
  • 安卓音频子系统之AudioFlinger
  • StringBuilder
  • 7大维度提升抖音资源获取效率:从单线程到企业级解决方案
  • PPTist免费开源在线PPT制作工具:三大核心功能与五分钟上手终极指南
  • Bidili Generator开源镜像:支持BF16/LoRA热插拔/显存治理的全栈开源项目
  • 9大优势打造专业排版:Outfit开源无衬线字体全解析
  • 5步打造无缝手写工作流:Xournal++高效数字笔记解决方案
  • AudioLDM-S音效质量调优:Duration/Steps/Batch Size三参数协同优化指南
  • 5分钟上手WebPlotDigitizer:从图表图像中提取数据的智能助手
  • 同一篇论文知网检测AI率每次不同?不是玄学是这个
  • SEO_新手必学的SEO优化基础教程与步骤详解(351 )
  • Graphormer效果展示:OGB基准SOTA分子属性预测结果实测分享
  • 基础架构架构师岗位技术手册
  • Spring IOC/DI 管理第三方Bean + 加载properties配置文件详解(Spring系列2)
  • 终极文件伪装指南:apate如何3秒内让你的文件“改头换面“
  • Full Page Screen Capture:智能滚动截图的完整网页保存方案 | 开发者与内容创作者必备
  • 为Windows 11 LTSC添加应用商店:3步实现系统功能完整化的创新解决方案
  • 【参数拟合】基于CMA-ES优化算法的OER模型全自动参数拟合系统附Matlab代码
  • 4大突破重构Web演示文稿创作体验:PPTist技术解析与实践指南
  • SRWE:突破Windows窗口限制的全能尺寸编辑工具
  • YahooFinanceApi:构建金融数据系统的三个核心挑战与解决方案
  • Qwen3-ForcedAligner应用案例:如何为口播视频快速添加精准字幕
  • 代码随想录一刷记录Day15——leetcode110.平衡二叉树 257. 二叉树的所有路径
  • GHelper终极指南:轻量级华硕笔记本性能控制工具完全解析
  • 如何在10分钟内掌握Stanford CoreNLP:自然语言处理工具包的终极实战指南
  • 如何突破语言壁垒?智能翻译跨语言工具的技术实现与场景化解决方案
  • 暗黑破坏神2存档编辑器终极指南:5分钟解放你的游戏体验
  • 26年知网AIGC检测算法大升级,这些变化你知道吗?