命令模式

命令模式:解耦请求与执行的行为设计艺术

一、命令模式的定义与核心思想

命令模式(Command Pattern) 是一种行为型设计模式,它将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分离。其核心思想是 “请求的抽象化”—— 通过引入命令对象作为中间媒介,让请求发送者(客户端)无需知晓请求的具体执行细节(如执行对象、执行逻辑),只需调用命令对象的统一方法即可触发执行。

简单来说,命令模式解决了 “请求发送者与接收者之间的紧耦合” 问题。例如,遥控器控制家电、GUI 中的按钮操作、撤销 / 重做功能,底层都依赖命令模式的设计思路,实现了请求发送者与接收者的解耦。

二、命令模式的角色构成

命令模式通常包含以下 5 个核心角色,各角色职责清晰、分工协作:

角色名称 核心职责 示例(遥控器控制电灯场景)
命令接口(Command) 定义执行请求的统一方法(如 execute()),是所有具体命令的抽象 LightCommand 接口
具体命令(ConcreteCommand) 实现命令接口,封装具体的请求逻辑:持有接收者对象,调用接收者的相关方法完成请求 TurnOnLightCommand(开灯命令)、TurnOffLightCommand(关灯命令)
接收者(Receiver) 实际执行请求的对象,包含完成请求所需的业务逻辑 Light 类(提供 turnOn()turnOff() 方法)
调用者(Invoker) 持有命令对象,负责触发命令的执行(无需知晓命令的具体实现) RemoteControl(遥控器,提供 pressButton() 方法)
客户端(Client) 创建具体命令对象和接收者对象,将接收者传入命令对象,再将命令对象传入调用者,完成组装 测试代码中的 main 方法

三、命令模式的工作原理与实现

1. 工作流程

  1. 客户端创建接收者对象(如电灯)和具体命令对象(如开灯命令),并将接收者注入具体命令;

  2. 客户端将具体命令对象传入调用者(如遥控器);

  3. 调用者通过调用命令对象的统一 execute() 方法,触发具体命令的执行;

  4. 具体命令对象调用接收者的对应方法,完成实际业务逻辑(如电灯亮起)。

2. 基础实现(遥控器控制电灯)

(1)命令接口(Command)
// 命令接口:定义统一的执行方法

public interface Command {

   void execute(); // 执行命令

}
(2)接收者:电灯类
// 接收者:实际执行命令的对象(电灯)

public class Light {

   private String name; // 电灯名称

   public Light(String name) {

       this.name = name;

   }

   // 接收者的业务方法:开灯

   public void turnOn() {

       System.out.println(name + ":电灯已打开,亮起~");

   }

   // 接收者的业务方法:关灯

   public void turnOff() {

       System.out.println(name + ":电灯已关闭,熄灭~");

   }

}
(3)具体命令
① 开灯命令
// 具体命令:开灯命令

public class TurnOnLightCommand implements Command {

   private Light light; // 持有接收者对象(电灯)

   // 构造器注入接收者

   public TurnOnLightCommand(Light light) {

       this.light = light;

   }

   @Override

   public void execute() {

       light.turnOn(); // 调用接收者的开灯方法

   }

}
② 关灯命令
// 具体命令:关灯命令

public class TurnOffLightCommand implements Command {

   private Light light; // 持有接收者对象(电灯)

   // 构造器注入接收者

   public TurnOffLightCommand(Light light) {

       this.light = light;

   }

   @Override

   public void execute() {

       light.turnOff(); // 调用接收者的关灯方法

   }

}
(4)调用者:遥控器类
// 调用者:遥控器(持有命令对象,触发命令执行)

public class RemoteControl {

   private Command command; // 持有当前命令对象

   // 设置命令(支持动态切换命令)

   public void setCommand(Command command) {

       this.command = command;

   }

   // 触发命令执行(按钮按下动作)

   public void pressButton() {

       if (command != null) {

           command.execute();

       } else {

           System.out.println("未设置命令,无法执行操作~");

       }

   }

}
(5)客户端测试
public class Client {

   public static void main(String[] args) {

       // 1. 创建接收者:客厅电灯

       Light livingRoomLight = new Light("客厅电灯");

       // 2. 创建具体命令:开灯命令、关灯命令(注入接收者)

       Command turnOnCommand = new TurnOnLightCommand(livingRoomLight);

       Command turnOffCommand = new TurnOffLightCommand(livingRoomLight);

       // 3. 创建调用者:遥控器

       RemoteControl remote = new RemoteControl();

       // 4. 执行开灯命令

       remote.setCommand(turnOnCommand);

       System.out.println("按下遥控器开关键:");

       remote.pressButton();

       // 5. 执行关灯命令

       remote.setCommand(turnOffCommand);

       System.out.println("n再次按下遥控器开关键:");

       remote.pressButton();

   }

}
输出结果
按下遥控器开关键:

客厅电灯:电灯已打开,亮起~

再次按下遥控器开关键:

客厅电灯:电灯已关闭,熄灭~

3. 扩展:支持撤销(Undo)功能

命令模式的核心优势之一是易于扩展 “撤销 / 重做”。只需在命令接口中新增 undo() 方法,具体命令实现该方法即可:

(1)扩展命令接口
public interface Command {

   void execute(); // 执行命令

   void undo(); // 撤销命令

}
(2)修改具体命令类
① 开灯命令(支持撤销)
public class TurnOnLightCommand implements Command {

   private Light light;

   public TurnOnLightCommand(Light light) {

       this.light = light;

   }

   @Override

   public void execute() {

       light.turnOn();

   }

   @Override

   public void undo() {

       light.turnOff(); // 撤销开灯:执行关灯操作

   }

}
② 关灯命令(支持撤销)
public class TurnOffLightCommand implements Command {

   private Light light;

   public TurnOffLightCommand(Light light) {

       this.light = light;

   }

   @Override

   public void execute() {

       light.turnOff();

   }

   @Override

   public void undo() {

       light.turnOn(); // 撤销关灯:执行开灯操作

   }

}
(3)修改调用者(记录历史命令)
public class RemoteControl {

   private Command currentCommand; // 当前命令

   private Command lastCommand; // 记录上一次执行的命令(用于撤销)

   public void setCommand(Command command) {

       this.currentCommand = command;

   }

   public void pressButton() {

       if (currentCommand != null) {

           currentCommand.execute();

           lastCommand = currentCommand; // 记录执行过的命令

       } else {

           System.out.println("未设置命令,无法执行操作~");

       }

   }

   // 撤销操作

   public void pressUndoButton() {

       if (lastCommand != null) {

           System.out.println("执行撤销操作:");

           lastCommand.undo();

           lastCommand = null; // 撤销后清空历史命令(简化实现)

       } else {

           System.out.println("无历史命令可撤销~");

       }

   }

}
(4)扩展客户端测试
public class Client {

   public static void main(String[] args) {

       Light livingRoomLight = new Light("客厅电灯");

       Command turnOnCommand = new TurnOnLightCommand(livingRoomLight);

       Command turnOffCommand = new TurnOffLightCommand(livingRoomLight);

       RemoteControl remote = new RemoteControl();

       // 执行开灯 -> 撤销开灯

       remote.setCommand(turnOnCommand);

       System.out.println("按下遥控器开关键:");

       remote.pressButton();

       remote.pressUndoButton();

       // 执行关灯 -> 撤销关灯

       System.out.println("n按下遥控器关关键:");

       remote.setCommand(turnOffCommand);

       remote.pressButton();

       remote.pressUndoButton();

   }

}
扩展输出结果
按下遥控器开关键:

客厅电灯:电灯已打开,亮起~

执行撤销操作:

客厅电灯:电灯已关闭,熄灭~

按下遥控器关关键:

客厅电灯:电灯已关闭,熄灭~

执行撤销操作:

客厅电灯:电灯已打开,亮起~

四、命令模式的应用场景

命令模式适用于以下场景:

  1. 请求发送者与接收者解耦:例如,GUI 中的按钮(发送者)无需知道具体执行逻辑(如打开文件、保存文件),只需关联对应命令即可。

  2. 需要支持撤销 / 重做操作:例如,文本编辑器的撤销(Ctrl+Z)、重做(Ctrl+Y),命令模式可通过记录命令历史实现。

  3. 需要动态切换命令:例如,遥控器可切换控制电灯、电视、空调等不同设备,只需更换命令对象即可。

  4. 需要批量执行命令(命令队列):例如,批处理任务(如批量备份文件),可将多个命令存入队列,依次执行。

  5. 需要日志记录命令执行:例如,数据库事务的日志恢复,记录所有执行过的命令,异常时通过命令回滚数据。

五、命令模式的优缺点

优点

  1. 解耦请求发送者与接收者:发送者无需知道接收者的存在,也无需了解执行逻辑,只需调用命令的统一方法,符合 “开闭原则”。

  2. 易于扩展新命令:新增命令只需实现命令接口,无需修改原有调用者和接收者代码,扩展性强。

  3. 支持撤销 / 重做、命令队列等高级功能:通过记录命令历史或组合命令,可轻松实现复杂操作。

  4. 便于管理命令执行:可对命令进行日志记录、事务管理(如批量执行、异常回滚)。

缺点

  1. 类数量增多:每个命令都需要对应一个具体命令类,若命令较多,会导致系统中类的数量激增,增加维护成本。

  2. 简单场景下冗余:对于简单的请求(如直接调用一个方法),使用命令模式会增加不必要的复杂性。

  3. 命令对象可能占用较多内存:若需要记录大量命令历史(如撤销多次操作),会占用一定的内存空间。

六、命令模式与其他模式的区别

  1. 与策略模式
  • 命令模式:聚焦 “请求的封装与解耦”,核心是 “谁发起、谁执行” 的分离;

  • 策略模式:聚焦 “算法的替换”,核心是 “如何执行” 的灵活切换(发送者知晓接收者)。

  1. 与观察者模式
  • 命令模式:是 “一对一” 的请求传递(调用者 -> 命令 -> 接收者);

  • 观察者模式:是 “一对多” 的事件通知(主题 -> 多个观察者)。

posted @ 2025-12-10 00:51  圣祖帝皇  阅读(3)  评论(0)    收藏  举报