0%

命令模式

命令模式(Command Pattern):封装请求的艺术

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

命令模式的核心结构

命令模式通过四个核心角色实现请求的封装与解耦,分工明确且扩展性强:

命令接口(Command)

  • 定义命令的抽象接口,声明执行命令的方法(通常为execute()),是所有具体命令的父类。
  • 示例:OrderCommand(订单命令接口,声明execute()方法)。

具体命令(ConcreteCommand)

  • 实现命令接口,封装具体的请求逻辑,通常持有接收者(Receiver)的引用,并在execute()方法中调用接收者的相关方法完成请求。
  • 示例:PayOrderCommand(支付订单命令)、CancelOrderCommand(取消订单命令)。

接收者(Receiver)

  • 真正执行命令的对象,包含完成命令所需的业务逻辑(如支付、取消等操作)。
  • 示例:OrderService(订单服务,提供pay()cancel()等方法)。

调用者(Invoker)

  • 触发命令执行的对象,持有命令对象的引用,通过调用命令的execute()方法触发请求,不直接与接收者交互。
  • 示例:OrderController(订单控制器,接收用户请求并触发对应命令)。

代码实现示例

以 “订单管理系统” 为例,展示命令模式的实现:支持支付订单和取消订单操作,通过命令对象封装这些操作,实现解耦和日志记录。

1. 命令接口与具体命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 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;
}

@Override
public void execute() {
receiver.pay(orderId); // 调用接收者的支付方法
this.log = "订单[" + orderId + "]已支付";
}

@Override
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;
}

@Override
public void execute() {
receiver.cancel(orderId); // 调用接收者的取消方法
this.log = "订单[" + orderId + "]已取消";
}

@Override
public String getLog() {
return log;
}
}

2. 接收者与调用者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 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. 客户端使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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]已取消
}
}

命令模式的核心优势

  1. 解耦发送者与接收者
    调用者(如OrderController)无需知道接收者(如OrderService)的具体实现,只需通过命令对象间接调用,降低了两者的耦合度。
  2. 支持命令的灵活管理
    • 参数化命令:命令对象可携带参数(如orderId),使调用者能动态指定命令的目标。
    • 命令排队:可将多个命令存储在队列中,按顺序执行(如批处理任务)。
    • 日志记录:通过命令的getLog()方法记录操作日志,便于审计和追踪。
    • 撤销操作:扩展命令接口为UndoableCommand,添加undo()方法,实现命令的反向操作(如撤销支付)。
  3. 易于扩展新命令
    新增命令时只需实现Command接口,无需修改调用者或接收者的代码,符合开闭原则(如新增 “退款命令”RefundCommand)。

适用场景

  1. 需要抽象请求的场景
    当需要将请求参数化(如不同订单执行不同操作)、存储请求(如任务队列)或延迟执行请求(如定时任务)时。
  2. 需要支持撤销 / 重做的场景
    如文本编辑器的撤销(Ctrl+Z)、绘图软件的操作回退,通过命令对象记录操作历史,实现反向执行。
  3. 需要日志记录或事务管理的场景
    如数据库事务(执行一组命令,成功则提交,失败则回滚)、操作审计日志(记录所有命令的执行情况)。
  4. GUI 中的事件处理
    如按钮点击事件:按钮(调用者)触发命令,命令封装具体操作(如打开窗口、提交表单),与业务逻辑解耦。

优缺点分析

优点

  • 低耦合:发送者与接收者通过命令间接交互,互不依赖具体实现。
  • 高灵活性:支持命令的动态切换、排队、撤销等高级功能。
  • 易扩展:新增命令只需添加实现类,对现有代码无侵入。

缺点

  • 类数量膨胀:每个命令都需对应一个具体命令类,若命令过多,会导致类数量增加。
  • 逻辑间接性:请求的执行需要经过 “调用者→命令→接收者” 的多层传递,可能增加调试难度。

经典应用案例

  1. GUI 框架的事件机制
    如 Swing 的Action接口(命令模式的实现):按钮点击时触发Action(命令),Action封装具体操作(如保存文件),与按钮解耦。
  2. 命令行工具的命令解析
    git命令:git commitgit push等命令均封装为独立的命令对象,可支持参数化、日志记录和撤销(git revert)。
  3. 事务管理
    数据库事务中,一组 SQL 操作被封装为命令,提交时批量执行,失败时通过命令的undo()方法回滚。
  4. 遥控器设计
    电视遥控器(调用者)的每个按钮对应一个命令(如开机、换台),命令封装对电视(接收者)的具体操作,支持灵活更换命令。

总结

命令模式通过将请求封装为对象,实现了发送者与接收者的解耦,并支持命令的灵活管理(排队、日志、撤销等)。其核心价值在于将 “做什么” 与 “谁去做” 分离,使系统更易于扩展和维护。在需要抽象请求、支持复杂操作管理的场景中,命令模式是一种优雅的解决方案

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10