设计模式17-命令模式:请求的封装与回放
2026/9/11 18:56:07 网站建设 项目流程

命令模式:把请求变成可以保存和撤销的对象

点击按钮、操作队列、撤销重做、任务调度,都要求“动作”本身能像数据一样被保存和传递。命令模式把动作对象化。

一、痛点:调用者直接绑死接收者

一个智能家居系统里,用户可能通过面板、语音、定时任务三种方式执行“开灯”“关灯”“调节温度”。如果每种入口都直接调用设备方法:

if("light-on".equals(action)){light.turnOn();}elseif("ac-up".equals(action)){ac.increaseTemperature();}

调用者和设备实现强耦合,也没法支持撤销、排队、日志回放。

命令模式的回答:把每个动作封装成 Command 对象,调用者只向命令对象发出“执行”,不关心谁真正干活

二、实现:命令接口 → 具体命令 → 调用者 → 接收者

第一步:定义命令接口

publicinterfaceCommand{voidexecute();voidundo();}

第二步:定义接收者

publicclassLight{privatebooleanon;publicvoidturnOn(){this.on=true;System.out.println("[灯] 已打开");}publicvoidturnOff(){this.on=false;System.out.println("[灯] 已关闭");}}

第三步:实现具体命令

publicclassLightOnCommandimplementsCommand{privatefinalLightlight;publicLightOnCommand(Lightlight){this.light=light;}@Overridepublicvoidexecute(){light.turnOn();}@Overridepublicvoidundo(){light.turnOff();}}
publicclassLightOffCommandimplementsCommand{privatefinalLightlight;publicLightOffCommand(Lightlight){this.light=light;}@Overridepublicvoidexecute(){light.turnOff();}@Overridepublicvoidundo(){light.turnOn();}}

第四步:调用者与历史记录

importjava.util.ArrayDeque;importjava.util.Deque;publicclassRemoteController{privatefinalDeque<Command>history=newArrayDeque<>();publicvoidpress(Commandcommand){command.execute();history.push(command);}publicvoidundo(){if(!history.isEmpty()){history.pop().undo();}}}

第五步:不同入口复用同一套命令

Lightlight=newLight();RemoteControllercontroller=newRemoteController();controller.press(newLightOnCommand(light));controller.press(newLightOffCommand(light));controller.undo();// 重新开灯

面板、语音、定时任务都只是创建同一个LightOnCommand的入口,设备逻辑只存在一处。

三、命令模式解决的四类问题

问题命令模式的解法
调用者与执行者耦合调用者只依赖 Command 接口
需要撤销/重做命令对象保存历史,并可执行 undo
需要延迟执行或定时执行命令对象可以被排队、持久化、定时触发
需要记录操作日志每个命令天然是日志条目

四、工程化提醒

  1. 只有动作需要被管理时才引入命令模式:普通业务方法直接调用即可,别把所有调用都包装成 Command;
  2. 命令粒度要合适:一个命令最好对应一个可撤销的业务动作,太细会类爆炸,太粗会难回滚;
  3. 与真实队列配合:生产上可将 Command 序列化到消息队列,实现任务解耦和重试。

小结

命令模式一句话:把动作封装成对象,让动作可保存、可排队、可撤销。它解耦调用者与接收者,也为复杂操作管理提供了统一入口。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询