命令模式
命令模式(Command Pattern):封装请求的艺术
命令模式是行为型设计模式的一种,核心思想是将请求(方法调用)封装为一个独立的对象,使请求的发送者与接收者解耦。通过命令对象,可实现请求的参数化、排队、日志记录及撤销等功能,本质是 “将操作封装为对象,实现请求的灵活管理”。
命令模式的核心结构

命令模式通过四个核心角色实现请求的封装与解耦,分工明确且扩展性强:
命令接口(Command)
- 定义命令的抽象接口,声明执行命令的方法(通常为
execute()),是所有具体命令的父类。 - 示例:
OrderCommand(订单命令接口,声明execute()方法)。
具体命令(ConcreteCommand)
- 实现命令接口,封装具体的请求逻辑,通常持有接收者(Receiver)的引用,并在
execute()方法中调用接收者的相关方法完成请求。 - 示例:
PayOrderCommand(支付订单命令)、CancelOrderCommand(取消订单命令)。
接收者(Receiver)
- 真正执行命令的对象,包含完成命令所需的业务逻辑(如支付、取消等操作)。
- 示例:
OrderService(订单服务,提供pay()、cancel()等方法)。
调用者(Invoker)
- 触发命令执行的对象,持有命令对象的引用,通过调用命令的
execute()方法触发请求,不直接与接收者交互。 - 示例:
OrderController(订单控制器,接收用户请求并触发对应命令)。
代码实现示例
以 “订单管理系统” 为例,展示命令模式的实现:支持支付订单和取消订单操作,通过命令对象封装这些操作,实现解耦和日志记录。
1. 命令接口与具体命令
// 1. 命令接口
public interface Command {
void execute(); // 执行命令
String getLog(); // 获取命令日志(扩展功能)
}
// 2. 具体命令1:支付订单
public class PayOrderCommand implements Command {
private OrderService receiver; // 接收者
private String orderId; // 命令参数
private String log; // 命令日志
public PayOrderCommand(OrderService receiver, String orderId) {
this.receiver = receiver;
this.orderId = orderId;
}
public void execute() {
receiver.pay(orderId); // 调用接收者的支付方法
this.log = "订单[" + orderId + "]已支付";
}
public String getLog() {
return log;
}
}
// 3. 具体命令2:取消订单
public class CancelOrderCommand implements Command {
private OrderService receiver;
private String orderId;
private String log;
public CancelOrderCommand(OrderService receiver, String orderId) {
this.receiver = receiver;
this.orderId = orderId;
}
public void execute() {
receiver.cancel(orderId); // 调用接收者的取消方法
this.log = "订单[" + orderId + "]已取消";
}
public String getLog() {
return log;
}
}
2. 接收者与调用者
// 4. 接收者:订单服务(真正执行命令)
public class OrderService {
// 支付订单
public void pay(String orderId) {
System.out.println("执行支付逻辑:订单[" + orderId + "]支付成功");
}
// 取消订单
public void cancel(String orderId) {
System.out.println("执行取消逻辑:订单[" + orderId + "]取消成功");
}
}
// 5. 调用者:订单控制器(触发命令)
public class OrderController {
private Command command; // 持有命令对象
// 设置命令(动态切换命令)
public void setCommand(Command command) {
this.command = command;
}
// 执行命令
public void executeCommand() {
if (command != null) {
command.execute();
}
}
// 获取命令日志
public String getCommandLog() {
return command != null ? command.getLog() : "";
}
}
3. 客户端使用
public class CommandDemo {
public static void main(String[] args) {
// 创建接收者
OrderService orderService = new OrderService();
// 创建具体命令(封装请求)
Command payCommand = new PayOrderCommand(orderService, "ORDER_001");
Command cancelCommand = new CancelOrderCommand(orderService, "ORDER_001");
// 创建调用者并执行命令
OrderController controller = new OrderController();
// 执行支付命令
controller.setCommand(payCommand);
controller.executeCommand();
System.out.println("日志:" + controller.getCommandLog());
// 输出:
// 执行支付逻辑:订单[ORDER_001]支付成功
// 日志:订单[ORDER_001]已支付
// 执行取消命令
controller.setCommand(cancelCommand);
controller.executeCommand();
System.out.println("日志:" + controller.getCommandLog());
// 输出:
// 执行取消逻辑:订单[ORDER_001]取消成功
// 日志:订单[ORDER_001]已取消
}
}
命令模式的核心优势
- 解耦发送者与接收者
调用者(如OrderController)无需知道接收者(如OrderService)的具体实现,只需通过命令对象间接调用,降低了两者的耦合度。 - 支持命令的灵活管理
- 参数化命令:命令对象可携带参数(如
orderId),使调用者能动态指定命令的目标。 - 命令排队:可将多个命令存储在队列中,按顺序执行(如批处理任务)。
- 日志记录:通过命令的
getLog()方法记录操作日志,便于审计和追踪。 - 撤销操作:扩展命令接口为
UndoableCommand,添加undo()方法,实现命令的反向操作(如撤销支付)。
- 参数化命令:命令对象可携带参数(如
- 易于扩展新命令
新增命令时只需实现Command接口,无需修改调用者或接收者的代码,符合开闭原则(如新增 “退款命令”RefundCommand)。
适用场景
- 需要抽象请求的场景
当需要将请求参数化(如不同订单执行不同操作)、存储请求(如任务队列)或延迟执行请求(如定时任务)时。 - 需要支持撤销 / 重做的场景
如文本编辑器的撤销(Ctrl+Z)、绘图软件的操作回退,通过命令对象记录操作历史,实现反向执行。 - 需要日志记录或事务管理的场景
如数据库事务(执行一组命令,成功则提交,失败则回滚)、操作审计日志(记录所有命令的执行情况)。 - GUI 中的事件处理
如按钮点击事件:按钮(调用者)触发命令,命令封装具体操作(如打开窗口、提交表单),与业务逻辑解耦。
优缺点分析
优点
- 低耦合:发送者与接收者通过命令间接交互,互不依赖具体实现。
- 高灵活性:支持命令的动态切换、排队、撤销等高级功能。
- 易扩展:新增命令只需添加实现类,对现有代码无侵入。
缺点
- 类数量膨胀:每个命令都需对应一个具体命令类,若命令过多,会导致类数量增加。
- 逻辑间接性:请求的执行需要经过 “调用者→命令→接收者” 的多层传递,可能增加调试难度。
经典应用案例
- GUI 框架的事件机制
如 Swing 的Action接口(命令模式的实现):按钮点击时触发Action(命令),Action封装具体操作(如保存文件),与按钮解耦。 - 命令行工具的命令解析
如git命令:git commit、git push等命令均封装为独立的命令对象,可支持参数化、日志记录和撤销(git revert)。 - 事务管理
数据库事务中,一组 SQL 操作被封装为命令,提交时批量执行,失败时通过命令的undo()方法回滚。 - 遥控器设计
电视遥控器(调用者)的每个按钮对应一个命令(如开机、换台),命令封装对电视(接收者)的具体操作,支持灵活更换命令。
总结
命令模式通过将请求封装为对象,实现了发送者与接收者的解耦,并支持命令的灵活管理(排队、日志、撤销等)。其核心价值在于将 “做什么” 与 “谁去做” 分离,使系统更易于扩展和维护。在需要抽象请求、支持复杂操作管理的场景中,命令模式是一种优雅的解决方案