Java设计模式:命令模式的革命性解耦实践
摘要
命令模式(Command Pattern)是行为型设计模式的"解耦专家",它将请求封装为独立对象,使不同请求、队列或日志请求成为可能。本文将深入探讨命令模式的核心思想、实现方式及实际应用场景,通过丰富的Java代码示例展示其如何实现请求方与执行方的解耦,并分析撤销/重做、事务处理等高级应用场景。
一、命令模式核心思想
命令模式的核心是将"请求"封装成对象,通过参数化方式传递这些对象,支持以下关键特性:
- 请求封装:将操作封装为包含所有信息的独立对象
- 解耦调用者与执行者:调用者不直接调用接收者
- 支持操作序列化:命令可存储、传递、排队或记录
- 撤销/重做支持:记录历史命令实现逆向操作
应用场景:
- 需要将操作请求者与执行者解耦时
- 需要支持事务、撤销、重做功能
- 需要排队执行或延迟执行请求
- 需要宏命令(命令组合)
二、命令模式结构解析
UML类图示意
[Invoker] --> [Command]
[Command] <|-- [ConcreteCommand]
[Command] o--> [Receiver]
[ConcreteCommand] --> [Receiver]
核心组件角色
| 角色 | 职责描述 | 典型实现 |
|---|---|---|
| Command | 命令接口 | 包含execute()方法的接口 |
| ConcreteCommand | 具体命令 | 绑定接收者与操作的实现类 |
| Receiver | 命令接收者 | 实际执行操作的对象 |
| Invoker | 调用者 | 触发命令执行的控制器 |
| Client | 客户端 | 创建命令对象并设置接收者 |
三、基础实现:智能家居控制案例
// 命令接口
interface Command {
void execute();
void undo();
}
// 接收者:灯光
class Light {
void turnOn() {
System.out.println("Light is ON");
}
void turnOff() {
System.out.println("Light is OFF");
}
}
// 具体命令:开灯命令
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.turnOn();
}
public void undo() {
light.turnOff();
}
}
// 调用者:遥控器
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
public void pressUndo() {
command.undo();
}
}
// 客户端使用
public class SmartHomeDemo {
public static void main(String[] args) {
// 创建接收者
Light livingRoomLight = new Light();
// 创建具体命令
Command lightOn = new LightOnCommand(livingRoomLight);
// 设置调用者
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
// 执行命令
remote.pressButton(); // 开灯
remote.pressUndo(); // 关灯(撤销)
}
}
四、高级特性实现
1. 宏命令(命令组合)
class MacroCommand implements Command {
private List<Command> commands = new ArrayList<>();
public void addCommand(Command command) {
commands.add(command);
}
public void execute() {
for (Command cmd : commands) {
cmd.execute();
}
}
public void undo() {
// 按反向顺序撤销
for (int i = commands.size()-1; i >= 0; i--) {
commands.get(i).undo();
}
}
}
// 使用示例
MacroCommand partyMode = new MacroCommand();
partyMode.addCommand(new LightOnCommand(livingRoomLight));
partyMode.addCommand(new MusicPlayerCommand("Party Playlist"));
partyMode.addCommand(new ThermostatCommand(22));
remote.setCommand(partyMode);
remote.pressButton(); // 执行所有命令
2. 支持撤销的复杂命令
// 接收者:智能恒温器
class Thermostat {
private int currentTemp;
private int previousTemp;
public void setTemperature(int temp) {
previousTemp = currentTemp;
currentTemp = temp;
System.out.println("Temperature set to: " + temp + "°C");
}
public void restorePrevious() {
System.out.println("Restoring temperature to: " + previousTemp + "°C");
int temp = previousTemp;
previousTemp = currentTemp;
currentTemp = temp;
}
}
// 具体命令(支持撤销)
class ThermostatCommand implements Command {
private Thermostat thermostat;
private int targetTemp;
public ThermostatCommand(Thermostat thermostat, int temp) {
this.thermostat = thermostat;
this.targetTemp = temp;
}
public void execute() {
thermostat.setTemperature(targetTemp);
}
public void undo() {
thermostat.restorePrevious();
}
}
五、命令模式在复杂系统中的应用
1. 金融交易系统
// 事务命令接口
interface TransactionCommand extends Command {
void validate() throws TransactionException;
}
// 具体交易命令
class MoneyTransferCommand implements TransactionCommand {
private Account source;
private Account target;
private BigDecimal amount;
private BigDecimal preSourceBalance;
private BigDecimal preTargetBalance;
public MoneyTransferCommand(Account source, Account target, BigDecimal amount) {
this.source = source;
this.target = target;
this.amount = amount;
}
public void validate() throws InsufficientFundsException {
if (source.getBalance().compareTo(amount) < 0) {
throw new InsufficientFundsException();
}
}
public void execute() {
// 保存原始状态
preSourceBalance = source.getBalance();
preTargetBalance = target.getBalance();
// 执行转账
source.withdraw(amount);
target.deposit(amount);
}
public void undo() {
// 恢复原始状态
source.setBalance(preSourceBalance);
target.setBalance(preTargetBalance);
}
}
// 交易处理器
class TransactionProcessor {
private Stack<TransactionCommand> history = new Stack<>();
public void executeTransaction(TransactionCommand cmd) {
try {
cmd.validate();
cmd.execute();
history.push(cmd);
} catch (TransactionException e) {
System.out.println("Transaction failed: " + e.getMessage());
}
}
public void rollbackLastTransaction() {
if (!history.isEmpty()) {
TransactionCommand cmd = history.pop();
cmd.undo();
}
}
}
2. GUI编辑器操作栈
// 编辑器命令接口
interface EditorCommand {
void execute(Document doc);
void undo(Document doc);
}
// 文本插入命令
class InsertTextCommand implements EditorCommand {
private String text;
private int position;
public InsertTextCommand(String text, int position) {
this.text = text;
this.position = position;
}
public void execute(Document doc) {
doc.insertText(position, text);
}
public void undo(Document doc) {
doc.deleteText(position, text.length());
}
}
// 编辑器历史管理
class CommandHistory {
private Stack<EditorCommand> history = new Stack<>();
private Stack<EditorCommand> redoStack = new Stack<>();
public void executeCommand(EditorCommand cmd, Document doc) {
cmd.execute(doc);
history.push(cmd);
redoStack.clear(); // 清除重做栈
}
public void undo(Document doc) {
if (!history.isEmpty()) {
EditorCommand cmd = history.pop();
cmd.undo(doc);
redoStack.push(cmd);
}
}
public void redo(Document doc) {
if (!redoStack.isEmpty()) {
EditorCommand cmd = redoStack.pop();
cmd.execute(doc);
history.push(cmd);
}
}
}
六、命令模式优缺点分析
优点:
| 优点 | 说明 |
|---|---|
| 解耦调用者/执行者 | 调用者无需知道具体接收者 |
| 支持撤销/重做 | 内置历史记录能力 |
| 命令可组合 | 宏命令支持批处理 |
| 支持延迟执行 | 命令可排队或调度 |
| 高扩展性 | 新命令无需修改现有结构 |
缺点:
| 缺点 | 说明 |
|---|---|
| 类数量增加 | 每个命令需单独类 |
| 复杂度提升 | 命令管理需要额外结构 |
| 内存消耗 | 历史记录可能占用较大内存 |
| 性能开销 | 多层抽象引入间接调用 |
七、命令模式与其他模式对比
| 模式 | 目的 | 关键区别 |
|---|---|---|
| 命令 | 封装请求为对象 | 核心是请求对象化 |
| 策略 | 封装算法 | 侧重于算法替换 |
| 观察者 | 对象间通信 | 基于订阅通知机制 |
| 模板方法 | 算法框架 | 通过继承实现扩展 |
| 职责链 | 请求传递链 | 沿链传递直至被处理 |
八、命令模式最佳实践
1. 空命令简化空判断
// 空命令实现(Null Object模式)
class NoCommand implements Command {
public void execute() {}
public void undo() {}
}
// 在调用者中初始化
class RemoteControl {
private Command onCommand = new NoCommand();
public void setOnCommand(Command cmd) {
this.onCommand = cmd;
}
}
2. 命令异步执行
class CommandExecutor {
private ExecutorService executor = Executors.newFixedThreadPool(4);
private List<Command> history = Collections.synchronizedList(new ArrayList<>());
public void executeAsync(Command cmd) {
executor.submit(() -> {
cmd.execute();
history.add(cmd);
});
}
public void shutdown() {
executor.shutdown();
}
}
3. 命令序列化支持
// 可序列化命令接口
interface PersistentCommand extends Command, Serializable {
// 标记接口
}
// 保存命令状态到文件
void saveCommands(List<PersistentCommand> commands, String filename) {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(filename))) {
oos.writeObject(commands);
}
}
// 从文件加载命令
@SuppressWarnings("unchecked")
List<PersistentCommand> loadCommands(String filename) {
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(filename))) {
return (List<PersistentCommand>) ois.readObject();
}
}
九、命令模式在框架中的应用
Spring框架的JmsTemplate
// 消息发送命令封装
public class JmsMessageProducer {
private JmsTemplate jmsTemplate;
public void sendMessage(String destination, final String message) {
jmsTemplate.execute(session -> {
// 创建命令内部类
return session.createTextMessage(message);
}, true); // 参数化处理
}
}
Java线程池任务提交
ExecutorService executor = Executors.newCachedThreadPool();
// 封装Runnable为命令
executor.execute(() -> {
System.out.println("Executing command in thread: "
+ Thread.currentThread().getName());
});
// 封装Callable为命令
Future<String> future = executor.submit(() -> {
return "Result of asynchronous command";
});
十、命令模式扩展:响应式命令
结合RxJava的回调处理
class ReactiveCommand<T> {
private final Callable<T> operation;
public ReactiveCommand(Callable<T> operation) {
this.operation = operation;
}
public Observable<T> execute() {
return Observable.fromCallable(operation)
.onErrorReturn(error -> {
System.err.println("Command failed: " + error.getMessage());
return null; // 错误处理
});
}
}
// 使用示例
ReactiveCommand<Integer> calculateCmd = new ReactiveCommand<>(
() -> 10 * new Random().nextInt(100));
calculateCmd.execute()
.subscribe(
result -> System.out.println("Result: " + result),
error -> System.err.println("Error: " + error)
);
总结
命令模式通过将请求封装为对象,实现了请求发起者与执行者的彻底解耦,为解决复杂交互系统提供了强大架构支持。其核心价值体现在三个维度:
- 架构灵活性:解耦调用与执行,支持异步、队列、日志等机制
- 历史管理能力:内置撤销/重做支持,是实现事务系统的核心模式
- 扩展性:宏命令组合、新命令添加不影响现有结构
在实际应用中,命令模式特别适合需要实现操作回滚、命令调度、多级撤销的场景,如金融交易系统、图形编辑软件、智能家居控制等。但同时也要注意避免过度使用:简单场景中使用命令模式可能导致不必要的类膨胀和结构复杂化。
在现代开发中,命令模式正在以新形式演进:函数式编程的结合(Lambda命令)、响应式实现、分布式命令队列等。深刻理解命令模式的核心思想--"封装请求为对象",将帮助开发者构建出更加灵活、健壮的系统架构。
















