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

解耦的艺术:深入理解设计模式之命令模式

将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。

这是命令模式(Command Pattern)的经典定义。听起来有些抽象?别担心,让我们从一个真实的问题开始,逐步揭开它的神秘面纱。

一、一个头疼的问题:遥控器的设计

假设你正在为一个智能家居系统设计一个万能遥控器。这个遥控器有7个可编程的插槽(按钮),每个插槽可以控制不同的家电设备:灯、空调、音响、车库门等。

最初的设计可能是这样的:​

public class RemoteControl {private Light livingRoomLight;private AirConditioner bedroomAC;private Stereo livingRoomStereo;// ... 更多设备引用public void onButtonPressed(int slot) {switch(slot) {case 0:livingRoomLight.turnOn();break;case 1:bedroomAC.turnOn();break;case 2:livingRoomStereo.turnOn();break;// ... 更多case}}public void offButtonPressed(int slot) {switch(slot) {case 0:livingRoomLight.turnOff();break;case 1:bedroomAC.turnOff();break;case 2:livingRoomStereo.turnOff();break;// ... 更多case}}
}

这个设计有什么问题?​

  1. 紧耦合​:遥控器直接依赖于具体的设备类

  2. 难以扩展​:每增加一个新设备,都要修改RemoteControl类

  3. 违反开闭原则​:对扩展开放,但对修改不开放的原则被破坏

  4. 功能有限​:难以实现撤销、宏命令(一键执行多个命令)等高级功能

二、命令模式的解决方案

命令模式的核心思想是:​将"请求"封装成对象。这样,客户端不需要知道请求的具体细节,只需要调用命令对象的统一接口。

命令模式的 UML 结构
Client(客户端)|v
Invoker(调用者/触发者) → Command(命令接口)|                       | execute()v                       | undo()
Receiver(接收者/执行者)   ConcreteCommand(具体命令)
代码实现

1. 命令接口(Command Interface)​

public interface Command {void execute();void undo(); // 支持撤销操作
}

2. 具体命令(Concrete Commands)​

// 开灯命令
public class LightOnCommand implements Command {private Light light;public LightOnCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.turnOn();}@Overridepublic void undo() {light.turnOff();}
}// 关灯命令
public class LightOffCommand implements Command {private Light light;public LightOffCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.turnOff();}@Overridepublic void undo() {light.turnOn();}
}// 空调温度设置命令
public class SetTemperatureCommand implements Command {private AirConditioner ac;private int temperature;private int previousTemperature;public SetTemperatureCommand(AirConditioner ac, int temperature) {this.ac = ac;this.temperature = temperature;}@Overridepublic void execute() {previousTemperature = ac.getTemperature(); // 保存之前的状态ac.setTemperature(temperature);}@Overridepublic void undo() {ac.setTemperature(previousTemperature); // 恢复到之前的状态}
}

3. 接收者(Receivers)​

// 灯类
public class Light {private boolean isOn = false;public void turnOn() {isOn = true;System.out.println("灯已打开");}public void turnOff() {isOn = false;System.out.println("灯已关闭");}
}// 空调类
public class AirConditioner {private int temperature = 26;public void setTemperature(int temp) {this.temperature = temp;System.out.println("空调温度设置为: " + temp + "°C");}public int getTemperature() {return temperature;}
}

4. 调用者(Invoker) - 我们的遥控器

public class RemoteControl {private Command[] onCommands;private Command[] offCommands;private Command undoCommand; // 记录最后执行的命令,用于撤销public RemoteControl() {onCommands = new Command[7];offCommands = new Command[7];undoCommand = new NoCommand(); // 空命令,避免null检查// 初始化所有按钮为空命令Command noCommand = new NoCommand();for (int i = 0; i < 7; i++) {onCommands[i] = noCommand;offCommands[i] = noCommand;}}public void setCommand(int slot, Command onCommand, Command offCommand) {onCommands[slot] = onCommand;offCommands[slot] = offCommand;}public void onButtonWasPressed(int slot) {onCommands[slot].execute();undoCommand = onCommands[slot]; // 记录用于撤销}public void offButtonWasPressed(int slot) {offCommands[slot].execute();undoCommand = offCommands[slot]; // 记录用于撤销}public void undoButtonWasPressed() {undoCommand.undo(); // 执行撤销操作}
}// 空命令对象(Null Object模式)
public class NoCommand implements Command {@Overridepublic void execute() {// 什么都不做}@Overridepublic void undo() {// 什么都不做}
}

5. 客户端使用

public class SmartHomeApp {public static void main(String[] args) {// 创建接收者Light livingRoomLight = new Light();AirConditioner bedroomAC = new AirConditioner();// 创建命令对象Command lightOn = new LightOnCommand(livingRoomLight);Command lightOff = new LightOffCommand(livingRoomLight);Command acOn = new SetTemperatureCommand(bedroomAC, 22);Command acOff = new SetTemperatureCommand(bedroomAC, 26);// 创建调用者(遥控器)RemoteControl remote = new RemoteControl();// 设置命令到插槽remote.setCommand(0, lightOn, lightOff); // 插槽0控制灯remote.setCommand(1, acOn, acOff);       // 插槽1控制空调// 使用遥控器System.out.println("=== 测试遥控器 ===");remote.onButtonWasPressed(0);  // 开灯remote.onButtonWasPressed(1);  // 开空调(设置22度)remote.undoButtonWasPressed(); // 撤销:空调回到之前温度System.out.println("=== 测试宏命令 ===");// 创建宏命令:一键开启"影院模式"Command[] partyOn = {lightOn, acOn};Command partyModeOn = new MacroCommand(partyOn);remote.setCommand(2, partyModeOn, lightOff);remote.onButtonWasPressed(2); // 一键开启所有设备!}
}// 宏命令:一次执行多个命令
public class MacroCommand implements Command {private Command[] commands;public MacroCommand(Command[] commands) {this.commands = commands;}@Overridepublic void execute() {for (Command command : commands) {command.execute();}}@Overridepublic void undo() {// 按相反顺序撤销for (int i = commands.length - 1; i >= 0; i--) {commands[i].undo();}}
}

三、命令模式的强大之处

1. ​完美的解耦

调用者(遥控器)完全不知道接收者(具体设备)的存在,它只与命令接口交互。

2. ​易于扩展

添加新设备时,只需要创建新的命令类,无需修改现有代码。

3. ​支持高级功能
  • 撤销/重做​:记录命令历史

  • 事务处理​:一系列命令要么全部成功,要么全部失败

  • 宏命令​:组合多个命令

  • 日志记录​:记录所有执行过的命令

  • 队列请求​:将命令放入队列,延迟执行

4. ​支持异步执行

命令对象可以很容易地在不同线程中执行。

四、实际应用场景

1. ​GUI 操作
// 菜单项点击命令
JMenuItem saveItem = new JMenuItem("Save");
saveItem.addActionListener(new ActionListener() { // 这其实就是一个命令对象!@Overridepublic void actionPerformed(ActionEvent e) {// 执行保存操作}
});
2. ​数据库事务
public class Transaction {private List<Command> commands = new ArrayList<>();public void addCommand(Command cmd) {commands.add(cmd);}public void commit() {try {for (Command cmd : commands) {cmd.execute();}} catch (Exception e) {rollback(); // 执行失败,回滚throw e;}}public void rollback() {for (int i = commands.size() - 1; i >= 0; i--) {commands.get(i).undo();}}
}
3. ​游戏开发
// 游戏角色动作命令
public class MoveCommand implements Command {private GameCharacter character;private Direction direction;private Position previousPosition;@Overridepublic void execute() {previousPosition = character.getPosition();character.move(direction);}@Overridepublic void undo() {character.setPosition(previousPosition);}
}// 支持回放功能
public class GameReplay {private List<Command> commandHistory = new ArrayList<>();public void recordCommand(Command cmd) {commandHistory.add(cmd);}public void replay() {for (Command cmd : commandHistory) {cmd.execute();}}
}
4. ​Android 中的例子
// 异步任务命令
class NetworkRequestCommand(private val url: String,private val callback: (Result<String>) -> Unit
) : Command {override fun execute() {CoroutineScope(Dispatchers.IO).launch {try {val result = apiService.getData(url)withContext(Dispatchers.Main) {callback(Result.success(result))}} catch (e: Exception) {withContext(Dispatchers.Main) {callback(Result.failure(e))}}}}override fun undo() {// 取消网络请求}
}

五、与其他模式的关系

  • 与策略模式​:命令模式关注的是请求的封装和传递,策略模式关注的是算法的选择和替换。

  • 与备忘录模式​:可以结合使用来实现复杂的撤销功能。

  • 与责任链模式​:命令可以作为责任链中的处理对象。

六、最佳实践和注意事项

✅ ​使用场景​:
  • 需要将操作抽象为对象

  • 需要支持撤销/重做功能

  • 需要将请求排队或记录请求日志

  • 需要支持事务操作

⚠️ ​注意事项​:
  • 如果命令太简单,可能会产生过多的命令类

  • 需要考虑命令对象的生命周期管理

  • 复杂的撤销功能可能需要备忘录模式辅助

🎯 ​实践建议​:
// 使用Lambda简化简单命令(Java 8+)
remote.setCommand(0, () -> light.turnOn(), () -> light.turnOff()
);// 使用函数式接口(更简洁)
@FunctionalInterface
public interface SimpleCommand {void execute();
}// 对于不需要撤销的简单场景非常实用

七、总结

命令模式通过将请求封装成对象,实现了请求发送者与接收者的解耦,让软件设计更加灵活和可扩展。它就像是在请求的发送者和接收者之间架起了一座桥梁,让信息可以更加优雅地传递。

核心价值​:

  • ✅ ​解耦​:发送者与接收者分离

  • ✅ ​灵活​:容易扩展新命令

  • ✅ ​强大​:支持撤销、队列、日志等高级功能

  • ✅ ​复用​:命令对象可以在不同场景下复用

下次当你需要设计一个灵活的操作系统时,记得考虑命令模式——它能让你的代码像智能遥控器一样强大而优雅!

http://www.dtcms.com/a/507647.html

相关文章:

  • C# 设计模式——工厂模式
  • 配置电话交换机 3CX 对接微软 Teams 直接路由
  • 亚马逊云渠道商:如何配置 AWS 自动化快照?
  • [优选算法专题四.前缀和——NO.29 和为 K 的子数组]
  • Python Web框架深度对比:Django vs Flask vs FastAPI(含优缺点与选型策略)
  • 盲盒抽谷机小程序:打造个性化消费的梦幻舞台
  • 性能优化揭秘:将淘宝商品 API 响应时间从 500ms 优化到 50ms 的技术实践
  • 龙岩互联网抖音seo招商
  • C++ 智能指针 std::unique_ptr、std::shared_ptr、std::weak_ptr
  • 猿辅导Java面试真实经历与深度总结(三)
  • Doris 数据导入
  • 网站建设+泰安saas建站平台有哪些
  • 动态规划之两个字符组/两个数组的dp问题
  • 【AI论文】UniVideo:面向视频的统一理解、生成与编辑
  • 获取resources目录下静态资源的两种方式
  • 一个域名可以做几个网站吗最好加盟网站建设
  • Android 自定义 View 如何设置默认尺寸
  • C#技术栈
  • 广东建设监理网站如何查企业的工商信息
  • INT301 Bio-computation 生物计算(神经网络)Pt.2 监督学习模型:感知器(Perceptron)
  • 机器学习(4)多特征与向量化
  • stripe/paypal
  • 机器学习(5)特征缩放与梯度下降收敛
  • 英飞凌推出首款100V aec合格GaN晶体管
  • 李宏毅机器学习笔记27
  • 机器学习作业七
  • openEuler安装jdk,nginx,redis
  • ffmpeg 交叉编译
  • Python编程之面向对象
  • 建设一个网站大概费用门户网站开发工具