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

Java设计模式-命令模式

Java设计模式-命令模式

模式概述

命令模式简介

核心思想:将“请求”封装为独立的命令对象,使请求的发送者(调用者)与接收者(执行者)完全解耦。通过命令对象的统一接口,支持请求的参数化、队列化、日志记录及撤销/重做等扩展操作。

模式类型:行为型设计模式(关注对象间的交互与职责分配)。

作用

  • 解耦发送者与接收者:调用者只需持有命令对象,无需直接依赖具体接收者。
  • 支持灵活扩展:新增请求类型仅需添加新的命令类,符合“开闭原则”。
  • 实现复杂控制:通过命令队列、日志记录或事务管理,支持批量执行、撤销/重做等功能。

典型应用场景

  • GUI事件处理(如按钮点击触发菜单打开、窗口关闭等操作)。
  • 事务管理系统(如数据库事务的提交/回滚,通过命令对象记录操作步骤)。
  • 智能设备控制(如遥控器控制空调、电视,通过命令对象统一管理多设备操作)。
  • 消息队列(如将用户请求封装为命令对象,异步执行或持久化存储)。

我认为:命令模式是“将动作打包成对象”的艺术,让请求的发起者专注于“触发”,执行者专注于“实现”,中间通过命令对象灵活调度。

课程目标

  • 理解命令模式的核心思想和经典应用场景
  • 识别应用场景,使用命令模式解决功能要求
  • 了解命令模式的优缺点

核心组件

角色-职责表

角色职责示例类名
命令接口(Command)定义执行请求的统一接口(如execute()Command
具体命令(ConcreteCommand)实现命令接口,绑定具体接收者并实现执行逻辑LightOnCommandLightOffCommand
调用者(Invoker)持有命令对象,触发命令执行(如遥控器按钮)RemoteController
接收者(Receiver)实际执行请求的对象(如电灯、空调等具体设备)LightAirConditioner

类图

下面是一个简化的类图表示,展示了命令模式中的主要角色及其交互方式:

实现
实现
依赖
使用
使用
«interface»
Command
+execute()
+undo()
LightOnCommand
-Light receiver
+execute()
+undo()
LightOffCommand
-Light receiver
+execute()
+undo()
RemoteController
-Command currentCommand
+setCommand(Command)
+pressButton()
+pressUndoButton()
Light
+turnOn()
+turnOff()

传统实现 VS 命令模式

案例需求

案例背景:实现一个遥控器控制电灯的功能,支持“开灯”和“关灯”操作,后续可能扩展“调节亮度”“定时开关”等新功能。

传统实现(痛点版)

代码实现

// 传统实现:调用者直接依赖接收者
class Light {public void turnOn() {System.out.println("电灯:打开");}public void turnOff() {System.out.println("电灯:关闭");}
}// 遥控器直接调用Light的方法(强耦合)
class RemoteController {private Light light;public RemoteController(Light light) {this.light = light;}public void openLight() {light.turnOn(); // 直接调用接收者方法}public void closeLight() {light.turnOff(); // 直接调用接收者方法}
}// 使用示例
public class Client {public static void main(String[] args) {Light light = new Light();RemoteController remote = new RemoteController(light);remote.openLight();  // 电灯:打开remote.closeLight(); // 电灯:关闭}
}

痛点总结

  • 强耦合:遥控器(调用者)与电灯(接收者)直接绑定,新增操作(如调节亮度)需修改遥控器代码,违反开闭原则。
  • 功能扩展困难:无法统一管理多个操作(如批量执行“开灯+关空调”),也不支持撤销/重做。
  • 缺乏抽象:请求(开灯/关灯)未被封装为对象,难以进行队列调度或日志记录。

命令模式 实现(优雅版)

代码实现

// 1. 命令接口(定义执行和撤销方法)
interface Command {void execute();  // 执行命令void undo();     // 撤销命令(可选)
}// 2. 具体命令:开灯命令(绑定接收者Light)
class LightOnCommand implements Command {private Light receiver;public LightOnCommand(Light receiver) {this.receiver = receiver;}@Overridepublic void execute() {receiver.turnOn(); // 调用接收者的实际操作}@Overridepublic void undo() {receiver.turnOff(); // 撤销操作是关灯}
}// 3. 具体命令:关灯命令
class LightOffCommand implements Command {private Light receiver;public LightOffCommand(Light receiver) {this.receiver = receiver;}@Overridepublic void execute() {receiver.turnOff();}@Overridepublic void undo() {receiver.turnOn(); // 撤销操作是开灯}
}// 4. 调用者:遥控器(支持设置和触发命令)
class RemoteController {private Command currentCommand; // 当前选中的命令public void setCommand(Command cmd) {this.currentCommand = cmd;}public void pressButton() {currentCommand.execute(); // 触发命令执行}public void pressUndoButton() {currentCommand.undo(); // 触发撤销操作}
}// 5. 接收者:电灯(实际执行操作)
class Light {public void turnOn() {System.out.println("电灯:打开");}public void turnOff() {System.out.println("电灯:关闭");}
}// 使用示例
public class Client {public static void main(String[] args) {Light light = new Light();Command lightOn = new LightOnCommand(light);Command lightOff = new LightOffCommand(light);RemoteController remote = new RemoteController();remote.setCommand(lightOn);remote.pressButton();  // 输出:电灯:打开remote.setCommand(lightOff);remote.pressButton();  // 输出:电灯:关闭// 支持撤销(例如:误操作后回退)remote.setCommand(lightOn);remote.pressButton();  // 电灯:打开remote.pressUndoButton(); // 输出:电灯:关闭}
}

优势

  • 解耦调用者与接收者:遥控器(RemoteController)仅依赖Command接口,无需知道Light的具体实现。
  • 灵活扩展:新增“调节亮度”功能只需添加BrightnessAdjustCommand类,无需修改现有代码。
  • 支持复杂控制:通过命令队列(如批量执行多个命令)或日志记录(如持久化命令对象),可实现事务回滚、撤销/重做等功能。

局限

  • ​类数量增加​​:每个请求需定义一个具体命令类,可能导致系统类膨胀(可通过宏命令或组合模式优化)。
  • 过度设计风险:若需求简单(如仅需直接调用),引入命令模式会增加代码复杂度。

模式变体

  • 宏命令(Macro Command):将多个命令组合成一个复合命令(如“一键回家”场景:开灯+开空调+播放音乐),通过一次触发执行所有子命令。
  • 带事务的命令:记录命令执行前的状态(如数据库快照),通过undo()方法回滚到之前的状态,支持事务的原子性。
  • 异步命令:将命令对象放入线程池或消息队列异步执行(如电商系统中的“下单”请求,通过命令队列解耦前端与库存/支付服务)。
  • 日志命令:将命令序列化后持久化存储(如日志文件),用于系统崩溃后的恢复或操作审计。

最佳实践

建议理由
命令接口保持简洁仅定义execute()等核心方法,避免冗余方法增加实现复杂度。
接收者职责单一接收者应专注于执行具体操作(如Light仅处理电灯开关),不与命令逻辑耦合。
合理使用钩子方法在命令接口中添加可选方法(如isEnabled()),允许子类控制是否执行。
命令对象无状态若命令需频繁创建,可通过享元模式共享实例(如无状态的LightOnCommand)。
支持撤销时记录上下文undo()需要依赖执行时的状态(如修改前的数值),命令对象需保存相关上下文。

一句话总结

命令模式通过将请求封装为对象,实现了调用者与接收者的解耦,同时支持灵活扩展、批量执行及撤销/重做等高级功能,是构建可维护、可扩展系统的重要工具。

如果关注Java设计模式内容,可以查阅作者的其他Java设计模式系列文章。😊

http://www.xdnf.cn/news/18330.html

相关文章:

  • python的校园顺路代送系统
  • Day 40:训练和测试的规范写法
  • Flink实现Exactly-Once语义的完整技术分解
  • 利用无事务方式插入数据库解决并发插入问题(最小主键id思路)
  • idea进阶技能掌握, 自带HTTP测试工具HTTP client使用方法详解,完全可替代PostMan
  • 暖哇科技AI调查智能体上线,引领保险调查风控智能化升级
  • 【数据结构】排序算法全解析:概念与接口
  • RK android14 Setting一级菜单IR遥控器无法聚焦问题解决方法
  • Apache ShenYu和Nacos之间的通信原理
  • VPS海外节点性能监控全攻略:从基础配置到高级优化
  • Android 入门到实战(三):ViewPager及ViewPager2多页面布局
  • 数据预处理学习心得:从理论到实践的桥梁搭建
  • 比剪映更轻量!SolveigMM 视频无损剪切实战体验
  • 29.Linux rsync+inotify解决同步数据实时性
  • 3D检测笔记:相机模型与坐标变换
  • 详解 scikit-learn 数据预处理工具:从理论到实践
  • CS+ for CC编译超慢的问题该如何解决
  • Day23 双向链表
  • 计算机网络--HTTP协议
  • 亚马逊新品爆单策略:从传统困境到智能突破
  • 【Grafana】grafana-image-renderer配合python脚本实现仪表盘导出pdf
  • 给你的Unity编辑器添加实现类似 Odin 的 条件显示字段 (ShowIf/HideIf) 功能
  • word——如何给封面、目录、摘要、正文设置不同的页码
  • 路由器NAT的类型测定
  • vue:vue中的ref和reactive
  • 【LLMs篇】18:基于EasyR1的Qwen2.5-VL GRPO训练
  • 层在init中只为创建线性层,forward的对线性层中间加非线性运算。且分层定义是为了把原本一长个代码的初始化和运算放到一个组合中。
  • 机械革命电竞控制台一直加载无法点击故障
  • MySQL事务及原理详解
  • 牛津大学xDeepMind 自然语言处理(3)