深入理解设计模式之命令模式(Command Pattern)
一、引言
在软件开发中,我们经常需要将"请求"封装成对象。比如:操作系统的任务调度、事务管理的回滚机制、GUI按钮的点击操作、撤销/重做功能等。如果将请求的调用者和接收者直接耦合,会导致代码难以扩展和维护。
想象这样的场景:你正在开发一个智能家居系统,遥控器上有多个按钮,每个按钮可以控制不同的设备(灯、空调、电视等)。如果在遥控器类中直接调用各个设备的方法,当需要更换设备或增加新功能时,就必须修改遥控器的代码。
命令模式为这类问题提供了优雅的解决方案。它就像餐厅的点餐系统:顾客(调用者)不直接告诉厨师(接收者)做什么菜,而是通过订单(命令对象)。服务员把订单交给厨师,厨师按订单做菜。这样顾客和厨师解耦,订单还可以排队、撤销或重做。
本文将深入探讨命令模式的原理、实现方式,并结合撤销/重做、事务管理、任务队列等实际应用,以及Spring、线程池等框架中的命令模式,帮助你全面掌握这一重要的设计模式。
二、什么是命令模式
2.1 定义
命令模式(Command Pattern)是一种行为型设计模式,它将请求封装成一个对象,从而允许你用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。
2.2 核心思想
- 请求封装:将请求封装成命令对象
- 解耦调用者和接收者:调用者不需要知道接收者的具体实现
- 支持撤销/重做:命令对象可以保存状态,支持逆操作
- 支持队列和日志:命令可以排队执行,也可以记录日志
2.3 模式结构
命令模式结构:
┌─────────────┐ ┌─────────────────┐
│ Client │────────▶│ Invoker │ 调用者
└─────────────┘ │ (遥控器) │
├─────────────────┤
│- command │
│+ setCommand() │
│+ executeCommand()│
└────────┬────────┘
│ 持有
▼
┌─────────────────┐
│ <<interface>> │
│ Command │ 抽象命令
├─────────────────┤
│+ execute() │
│+ undo() │
└────────┬────────┘
△
│ 实现
┌────────┴────────┐
│ │
┌───────┴────────┐ ┌────┴──────────┐
│ConcreteCommand │ │ConcreteCommand│
│ A │ │ B │
├────────────────┤ ├───────────────┤
│- receiver │ │- receiver │
│+ execute() │ │+ execute() │
│+ undo() │ │+ undo() │
└───────┬────────┘ └───────┬───────┘
│ 调用 │
▼ ▼
┌───────────────┐ ┌──────────────┐
│ Receiver │ │ Receiver │
│ (灯) │ │ (空调) │
├───────────────┤ ├──────────────┤
│+ action() │ │+ action() │
└───────────────┘ └──────────────┘
执行流程:
Client → 创建Command并设置Receiver
→ 将Command设置给Invoker
→ Invoker调用Command.execute()
→ Command调用Receiver.action()
命令模式的本质:
将"做什么"(Command)和"谁来做"(Receiver)解耦
2.4 角色说明
1. Command(抽象命令)
- 定义命令接口,声明执行方法
2. ConcreteCommand(具体命令)
- 实现命令接口
- 持有接收者引用
- 实现execute()调用接收者的方法
3. Receiver(接收者)
- 真正执行业务逻辑的对象
4. Invoker(调用者)
- 持有命令对象
- 调用命令对象的execute()
5. Client(客户端)
- 创建命令对象
- 设置命令的接收者
三、基础示例
3.1 场景:智能家居遥控器
经典的命令模式示例:遥控器控制多个家电设备。
接收者(家电设备):
java
/**
* 接收者:灯
*/
public class Light {
private String location;
public Light(String location) {
this.location = location;
}
public void on() {
System.out.println(location + "的灯打开了");
}
public void off() {
System.out.println(location + "的灯关闭了");
}
}
/**
* 接收者:空调
*/
class AirConditioner {
private String location;
public AirConditioner(String location) {
this.location = location;
}
public void on() {
System.out.println(location + "的空调打开了");
}
public void off() {
System.out.println(location + "的空调关闭了");
}
public void setTemperature(int temperature) {
System.out.println(location + "的空调温度设置为: " + temperature + "°C");
}
}
/**
* 接收者:电视
*/
class TV {
private String location;
public TV(String location) {
this.location = location;
}
public void on() {
System.out.println(location + "的电视打开了");
}
public void off() {
System.out.println(location + "的电视关闭了");
}
public void setChannel(int channel) {
System.out.println(location + "的电视切换到频道: " + channel);
}
}
抽象命令:
java
/**
* 抽象命令接口
*/
public interface Command {
/**
* 执行命令
*/
void execute();
/**
* 撤销命令
*/
void undo();
}
具体命令:
java
/**
* 具体命令:打开灯
*/
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
@Override
public void undo() {
light.off();
}
}
/**
* 具体命令:关闭灯
*/
class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
@Override
public void undo() {
light.on();
}
}
/**
* 具体命令:打开空调
*/
class AirConditionerOnCommand implements Command {
private AirConditioner ac;
public AirConditionerOnCommand(AirConditioner ac) {
this.ac = ac;
}
@Override
public void execute() {
ac.on();
ac.setTemperature(24);
}
@Override
public void undo() {
ac.off();
}
}
/**
* 空命令(Null Object模式)
*/
class NoCommand implements Command {
@Override
public void execute() {
// 什么也不做
}
@Override
public void undo() {
// 什么也不做
}
}
调用者(遥控器):
java
/**
* 调用者:遥控器
*/
public class RemoteControl {
private Command[] onCommands;
private Command[] offCommands;
private Command undoCommand; // 记录上一个命令,用于撤销
public RemoteControl() {
onCommands = new Command[7];
offCommands = new Command[7];
Command noCommand = new NoCommand();
for (int i = 0; i < 7; i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
undoCommand = noCommand;
}
/**
* 设置按钮的命令
*/
public void setCommand(int slot, Command onCommand, Command offCommand) {
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
/**
* 按下ON按钮
*/
public void onButtonPressed(int slot) {
onCommands[slot].execute();
undoCommand = onCommands[slot];
}
/**
* 按下OFF按钮
*/
public void offButtonPressed(int slot) {
offCommands[slot].execute();
undoCommand = offCommands[slot];
}
/**
* 撤销按钮
*/
public void undoButtonPressed() {
undoCommand.undo();
}
/**
* 显示遥控器状态
*/
public void display() {
System.out.println("\n===== 遥控器状态 =====");
for (int i = 0; i < onCommands.length; i++) {
System.out.println("[插槽 " + i + "] " +
onCommands[i].getClass().getSimpleName() + " " +
offCommands[i].getClass().getSimpleName());
}
System.out.println("===================\n");
}
}
客户端使用:
java
public class RemoteControlDemo {
public static void main(String[] args) {
System.out.println("========== 智能家居遥控器 ==========\n");
// 创建接收者
Light livingRoomLight = new Light("客厅");
Light bedroomLight = new Light("卧室");
AirConditioner livingRoomAC = new AirConditioner("客厅");
TV livingRoomTV = new TV("客厅");
// 创建命令
LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);
LightOnCommand bedroomLightOn = new LightOnCommand(bedroomLight);
LightOffCommand bedroomLightOff = new LightOffCommand(bedroomLight);
AirConditionerOnCommand acOn = new AirConditionerOnCommand(livingRoomAC);
Command acOff = new Command() {
@Override
public void execute() {
livingRoomAC.off();
}
@Override
public void undo() {
livingRoomAC.on();
}
};
// 创建遥控器
RemoteControl remote = new RemoteControl();
// 设置命令到按钮
remote.setCommand(0, livingRoomLightOn, livingRoomLightOff);
remote.setCommand(1, bedroomLightOn, bedroomLightOff);
remote.setCommand(2, acOn, acOff);
// 显示遥控器状态
remote.display();
// 使用遥控器
System.out.println("===== 测试遥控器 =====");
remote.onButtonPressed(0); // 打开客厅灯
remote.offButtonPressed(0); // 关闭客厅灯
System.out.println();
remote.onButtonPressed(1); // 打开卧室灯
System.out.println();
remote.onButtonPressed(2); // 打开空调
System.out.println();
System.out.println("===== 测试撤销功能 =====");
remote.undoButtonPressed(); // 撤销(关闭空调)
}
}
3.2 场景:宏命令(批量执行)
宏命令可以一次执行多个命令。
java
/**
* 宏命令:包含多个子命令
*/
public class MacroCommand implements Command {
private Command[] commands;
public MacroCommand(Command[] commands) {
this.commands = commands;
}
@Override
public void execute() {
System.out.println("[宏命令] 开始执行...");
for (Command command : commands) {
command.execute();
}
System.out.println("[宏命令] 执行完成");
}
@Override
public void undo() {
System.out.println("[宏命令] 撤销所有命令...");
// 反向撤销
for (int i = commands.length - 1; i >= 0; i--) {
commands[i].undo();
}
System.out.println("[宏命令] 撤销完成");
}
}
/**
* 测试宏命令
*/
class MacroCommandDemo {
public static void main(String[] args) {
System.out.println("========== 宏命令测试 ==========\n");
// 创建设备
Light light = new Light("客厅");
TV tv = new TV("客厅");
AirConditioner ac = new AirConditioner("客厅");
// 创建命令
LightOnCommand lightOn = new LightOnCommand(light);
Command tvOn = new Command() {
@Override
public void execute() {
tv.on();
}
@Override
public void undo() {
tv.off();
}
};
AirConditionerOnCommand acOn = new AirConditionerOnCommand(ac);
// 创建宏命令:"回家模式"
Command[] partyOn = {lightOn, tvOn, acOn};
MacroCommand partyOnMacro = new MacroCommand(partyOn);
// 执行宏命令
System.out.println("激活回家模式:");
partyOnMacro.execute();
System.out.println("\n撤销回家模式:");
partyOnMacro.undo();
}
}
四、实际生产场景应用
4.1 场景:文本编辑器的撤销/重做
文本编辑器需要支持撤销和重做操作。
java
import java.util.Stack;
/**
* 文本编辑器
*/
class TextEditor {
private StringBuilder text;
public TextEditor() {
this.text = new StringBuilder();
}
public void insertText(int position, String newText) {
text.insert(position, newText);
System.out.println("插入文本: \"" + newText + "\" 在位置 " + position);
}
public void deleteText(int position, int length) {
text.delete(position, position + length);
System.out.println("删除文本: 从位置 " + position + " 删除 " + length + " 个字符");
}
public String getText() {
return text.toString();
}
public void display() {
System.out.println("当前文本: \"" + text.toString() + "\"");
}
}
/**
* 文本命令接口
*/
interface TextCommand {
void execute();
void undo();
}
/**
* 插入文本命令
*/
class InsertTextCommand implements TextCommand {
private TextEditor editor;
private int position;
private String text;
public InsertTextCommand(TextEditor editor, int position, String text) {
this.editor = editor;
this.position = position;
this.text = text;
}
@Override
public void execute() {
editor.insertText(position, text);
}
@Override
public void undo() {
editor.deleteText(position, text.length());
}
}
/**
* 删除文本命令
*/
class DeleteTextCommand implements TextCommand {
private TextEditor editor;
private int position;
private int length;
private String deletedText; // 保存被删除的文本,用于撤销
public DeleteTextCommand(TextEditor editor, int position, int length) {
this.editor = editor;
this.position = position;
this.length = length;
// 保存将要删除的文本
this.deletedText = editor.getText().substring(position, position + length);
}
@Override
public void execute() {
editor.deleteText(position, length);
}
@Override
public void undo() {
editor.insertText(position, deletedText);
}
}
/**
* 命令管理器(支持撤销/重做)
*/
class CommandManager {
private Stack<TextCommand> undoStack = new Stack<>();
private Stack<TextCommand> redoStack = new Stack<>();
public void executeCommand(TextCommand command) {
command.execute();
undoStack.push(command);
redoStack.clear(); // 执行新命令后清空重做栈
}
public void undo() {
if (!undoStack.isEmpty()) {
TextCommand command = undoStack.pop();
command.undo();
redoStack.push(command);
System.out.println("撤销操作");
} else {
System.out.println("没有可撤销的操作");
}
}
public void redo() {
if (!redoStack.isEmpty()) {
TextCommand command = redoStack.pop();
command.execute();
undoStack.push(command);
System.out.println("重做操作");
} else {
System.out.println("没有可重做的操作");
}
}
}
/**
* 测试文本编辑器
*/
class TextEditorDemo {
public static void main(String[] args) {
System.out.println("========== 文本编辑器撤销/重做 ==========\n");
TextEditor editor = new TextEditor();
CommandManager manager = new CommandManager();
// 插入文本
manager.executeCommand(new InsertTextCommand(editor, 0, "Hello"));
editor.display();
System.out.println();
manager.executeCommand(new InsertTextCommand(editor, 5, " World"));
editor.display();
System.out.println();
manager.executeCommand(new InsertTextCommand(editor, 11, "!"));
editor.display();
System.out.println();
// 撤销操作
System.out.println("===== 撤销操作 =====");
manager.undo();
editor.display();
System.out.println();
manager.undo();
editor.display();
System.out.println();
// 重做操作
System.out.println("===== 重做操作 =====");
manager.redo();
editor.display();
System.out.println();
// 删除操作
System.out.println("===== 删除操作 =====");
manager.executeCommand(new DeleteTextCommand(editor, 0, 5));
editor.display();
System.out.println();
// 撤销删除
System.out.println("===== 撤销删除 =====");
manager.undo();
editor.display();
}
}
4.2 场景:订单处理系统
订单处理涉及多个步骤,使用命令模式可以灵活控制。
java
/**
* 订单实体
*/
class Order {
private String orderId;
private String status;
private double amount;
public Order(String orderId, double amount) {
this.orderId = orderId;
this.amount = amount;
this.status = "待支付";
}
// Getters and Setters
public String getOrderId() { return orderId; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public double getAmount() { return amount; }
@Override
public String toString() {
return String.format("订单[%s] 状态: %s, 金额: %.2f", orderId, status, amount);
}
}
/**
* 订单命令接口
*/
interface OrderCommand {
void execute();
void undo();
}
/**
* 支付命令
*/
class PayOrderCommand implements OrderCommand {
private Order order;
public PayOrderCommand(Order order) {
this.order = order;
}
@Override
public void execute() {
System.out.println("[支付] 处理订单: " + order.getOrderId());
order.setStatus("已支付");
System.out.println(" 扣款: " + order.getAmount() + " 元");
System.out.println(" " + order);
}
@Override
public void undo() {
System.out.println("[退款] 订单: " + order.getOrderId());
order.setStatus("已退款");
System.out.println(" 退款: " + order.getAmount() + " 元");
System.out.println(" " + order);
}
}
/**
* 发货命令
*/
class ShipOrderCommand implements OrderCommand {
private Order order;
public ShipOrderCommand(Order order) {
this.order = order;
}
@Override
public void execute() {
System.out.println("[发货] 处理订单: " + order.getOrderId());
order.setStatus("已发货");
System.out.println(" 物流单号: EXP" + order.getOrderId());
System.out.println(" " + order);
}
@Override
public void undo() {
System.out.println("[取消发货] 订单: " + order.getOrderId());
order.setStatus("已支付");
System.out.println(" " + order);
}
}
/**
* 完成订单命令
*/
class CompleteOrderCommand implements OrderCommand {
private Order order;
public CompleteOrderCommand(Order order) {
this.order = order;
}
@Override
public void execute() {
System.out.println("[完成] 订单: " + order.getOrderId());
order.setStatus("已完成");
System.out.println(" " + order);
}
@Override
public void undo() {
System.out.println("[取消完成] 订单: " + order.getOrderId());
order.setStatus("已发货");
System.out.println(" " + order);
}
}
/**
* 订单处理器
*/
class OrderProcessor {
private Stack<OrderCommand> commandHistory = new Stack<>();
public void processCommand(OrderCommand command) {
command.execute();
commandHistory.push(command);
System.out.println();
}
public void rollback() {
if (!commandHistory.isEmpty()) {
OrderCommand command = commandHistory.pop();
command.undo();
System.out.println();
} else {
System.out.println("没有可回滚的操作\n");
}
}
}
/**
* 测试订单处理
*/
class OrderProcessingDemo {
public static void main(String[] args) {
System.out.println("========== 订单处理系统 ==========\n");
Order order = new Order("ORD20250119001", 299.00);
System.out.println("创建订单: " + order);
System.out.println();
OrderProcessor processor = new OrderProcessor();
// 正常流程
System.out.println("===== 正常处理流程 =====");
processor.processCommand(new PayOrderCommand(order));
processor.processCommand(new ShipOrderCommand(order));
processor.processCommand(new CompleteOrderCommand(order));
// 异常回滚
System.out.println("===== 异常回滚 =====");
System.out.println("用户申请退货,开始回滚...\n");
processor.rollback(); // 取消完成
processor.rollback(); // 取消发货
processor.rollback(); // 退款
}
}
4.3 场景:任务队列
将任务封装成命令对象,放入队列异步执行。
java
import java.util.LinkedList;
import java.util.Queue;
/**
* 任务接口
*/
interface Task {
void execute();
}
/**
* 邮件发送任务
*/
class EmailTask implements Task {
private String recipient;
private String subject;
public EmailTask(String recipient, String subject) {
this.recipient = recipient;
this.subject = subject;
}
@Override
public void execute() {
System.out.println("[邮件任务] 发送邮件");
System.out.println(" 收件人: " + recipient);
System.out.println(" 主题: " + subject);
// 模拟发送
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" 发送成功!");
}
}
/**
* 数据导入任务
*/
class DataImportTask implements Task {
private String fileName;
public DataImportTask(String fileName) {
this.fileName = fileName;
}
@Override
public void execute() {
System.out.println("[数据导入] 导入文件: " + fileName);
// 模拟导入
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" 导入完成!");
}
}
/**
* 任务队列
*/
class TaskQueue {
private Queue<Task> queue = new LinkedList<>();
private boolean running = false;
public void addTask(Task task) {
queue.offer(task);
System.out.println("[任务队列] 添加任务: " + task.getClass().getSimpleName());
}
public void start() {
if (running) {
System.out.println("[任务队列] 已经在运行中");
return;
}
running = true;
Thread worker = new Thread(() -> {
System.out.println("[任务队列] 开始处理任务...\n");
while (running || !queue.isEmpty()) {
Task task = queue.poll();
if (task != null) {
try {
task.execute();
System.out.println();
} catch (Exception e) {
System.err.println("任务执行失败: " + e.getMessage());
}
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
break;
}
}
}
System.out.println("[任务队列] 所有任务处理完成");
});
worker.start();
}
public void stop() {
running = false;
}
}
/**
* 测试任务队列
*/
class TaskQueueDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println("========== 任务队列系统 ==========\n");
TaskQueue queue = new TaskQueue();
// 添加任务
queue.addTask(new EmailTask("user1@example.com", "订单确认"));
queue.addTask(new DataImportTask("users.csv"));
queue.addTask(new EmailTask("user2@example.com", "发货通知"));
queue.addTask(new DataImportTask("orders.csv"));
System.out.println();
// 启动队列
queue.start();
// 等待任务完成
Thread.sleep(2000);
// 停止队列
queue.stop();
}
}
五、开源框架中的应用
5.1 JDK的Runnable接口
Runnable是命令模式的典型应用。
java
/**
* JDK Runnable接口示例
*
* Runnable接口就是命令模式:
* - Runnable是Command接口
* - run()是execute()方法
* - Thread是Invoker
*/
public class RunnableDemo {
public static void main(String[] args) {
System.out.println("========== Runnable命令模式 ==========\n");
// 创建命令对象
Runnable task1 = () -> {
System.out.println("[任务1] 执行中... 线程: " + Thread.currentThread().getName());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("[任务1] 完成");
};
Runnable task2 = () -> {
System.out.println("[任务2] 执行中... 线程: " + Thread.currentThread().getName());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("[任务2] 完成");
};
// Invoker(Thread)执行命令
Thread thread1 = new Thread(task1);
Thread thread2 = new Thread(task2);
thread1.start();
thread2.start();
// 线程池也是Invoker
System.out.println("\n使用线程池执行命令:");
java.util.concurrent.ExecutorService executor =
java.util.concurrent.Executors.newFixedThreadPool(2);
executor.execute(task1);
executor.execute(task2);
executor.shutdown();
}
}
5.2 Spring的JdbcTemplate
Spring的JdbcTemplate回调机制使用了命令模式。
java
/**
* Spring JdbcTemplate命令模式(简化版)
*
* JdbcTemplate使用回调来实现命令模式:
* - Callback接口是Command
* - JdbcTemplate是Invoker
*/
/**
* 回调接口(命令接口)
*/
interface ConnectionCallback<T> {
T doInConnection(java.sql.Connection conn) throws java.sql.SQLException;
}
/**
* 简化的JdbcTemplate
*/
class SimpleJdbcTemplate {
public <T> T execute(ConnectionCallback<T> action) {
java.sql.Connection conn = null;
try {
// 获取连接
conn = getConnection();
System.out.println("[JdbcTemplate] 获取数据库连接");
// 执行回调命令
T result = action.doInConnection(conn);
System.out.println("[JdbcTemplate] 命令执行完成");
return result;
} catch (Exception e) {
System.err.println("[JdbcTemplate] 执行失败: " + e.getMessage());
throw new RuntimeException(e);
} finally {
if (conn != null) {
try {
conn.close();
System.out.println("[JdbcTemplate] 关闭连接");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private java.sql.Connection getConnection() {
// 模拟获取连接
return null;
}
}
/**
* 测试JdbcTemplate
*/
class JdbcTemplateDemo {
public static void main(String[] args) {
System.out.println("========== Spring JdbcTemplate ==========\n");
SimpleJdbcTemplate jdbcTemplate = new SimpleJdbcTemplate();
// 使用命令模式执行查询
System.out.println("===== 执行查询命令 =====");
jdbcTemplate.execute(conn -> {
System.out.println(" 执行SQL: SELECT * FROM users");
// 实际会执行SQL并返回结果
return "查询结果";
});
System.out.println("\n===== 执行更新命令 =====");
jdbcTemplate.execute(conn -> {
System.out.println(" 执行SQL: UPDATE users SET status = 1");
return null;
});
}
}
/**
* 真实的Spring JdbcTemplate使用:
*
* jdbcTemplate.execute(new ConnectionCallback<List<User>>() {
* public List<User> doInConnection(Connection conn) throws SQLException {
* // 执行数据库操作
* }
* });
*
* 或Lambda方式:
* jdbcTemplate.execute(conn -> {
* // 执行数据库操作
* });
*/
六、命令模式的优缺点
6.1 优点
1. 解耦调用者和接收者
调用者不需要知道接收者的具体实现
通过命令对象作为中介
Invoker → Command → Receiver
调用者 命令 接收者
2. 易于扩展
添加新命令:实现Command接口即可
不影响现有代码
符合开闭原则
3. 支持撤销和重做
命令对象可以保存状态
实现undo()方法实现撤销
维护命令历史栈实现重做
4. 支持组合命令
宏命令可以包含多个子命令
实现批量操作
5. 支持队列和日志
命令对象可以排队执行
可以记录命令日志
支持事务和回滚
6.2 缺点
1. 增加类的数量
每个命令都需要一个类
系统中命令很多时,类数量激增
2. 增加系统复杂度
引入命令对象这一中间层
增加了理解和维护成本
3. 可能造成性能开销
创建大量命令对象
频繁的对象创建和销毁
七、最佳实践
7.1 合理使用空命令
java
/**
* 空命令模式(Null Object)
* 避免空指针判断
*/
class NoCommand implements Command {
@Override
public void execute() {
// 什么也不做
}
@Override
public void undo() {
// 什么也不做
}
}
// 使用
Command command = getCommand();
if (command == null) {
command = new NoCommand(); // 使用空命令代替null
}
command.execute(); // 无需判空
7.2 命令日志和持久化
java
/**
* 可序列化的命令
* 支持持久化和恢复
*/
class SerializableCommand implements Command, java.io.Serializable {
private static final long serialVersionUID = 1L;
private String commandName;
private Map<String, Object> parameters;
@Override
public void execute() {
// 执行并记录日志
System.out.println("执行命令: " + commandName);
// 可以将命令序列化到磁盘
}
@Override
public void undo() {
System.out.println("撤销命令: " + commandName);
}
}
7.3 使用Lambda简化命令
java
/**
* 使用Lambda表达式简化命令创建
*/
@FunctionalInterface
interface SimpleCommand {
void execute();
}
class LambdaCommandDemo {
public static void main(String[] args) {
// 不使用Lambda(传统方式)
SimpleCommand cmd1 = new SimpleCommand() {
@Override
public void execute() {
System.out.println("执行命令1");
}
};
// 使用Lambda
SimpleCommand cmd2 = () -> System.out.println("执行命令2");
// 方法引用
SimpleCommand cmd3 = System.out::println;
cmd1.execute();
cmd2.execute();
}
}
八、总结
8.1 核心要点
- 命令模式的本质:将请求封装成对象,解耦调用者和接收者
- 四个关键角色 :
- Command(命令接口)
- ConcreteCommand(具体命令)
- Invoker(调用者)
- Receiver(接收者)
- 适用场景 :
- 需要撤销/重做功能
- 需要将请求排队或记录日志
- 需要支持宏命令(批量操作)
- 需要解耦调用者和接收者
8.2 使用建议
选择命令模式的检查清单:
✓ 是否需要撤销/重做功能?
✓ 是否需要将请求排队执行?
✓ 是否需要记录操作日志?
✓ 是否需要参数化对象(用不同请求配置对象)?
✓ 调用者和接收者是否需要解耦?
如果有2个以上"是",建议使用命令模式!
8.3 与其他模式的对比
命令 vs 策略:
- 命令:封装请求(做什么)
- 策略:封装算法(怎么做)
命令 vs 备忘录:
- 命令:通过undo()撤销
- 备忘录:通过保存状态恢复
命令 vs 职责链:
- 命令:单个命令处理请求
- 职责链:多个处理器传递请求
8.4 实践经验
- 简单场景用Lambda:不需要撤销时,用Lambda简化
- 复杂场景用类:需要状态保存和撤销时,定义命令类
- 使用空命令模式:避免空指针判断
- 命令队列:异步任务、批处理使用命令队列
- 宏命令:批量操作使用组合命令