Java设计模式:命令模式的革命性解耦实践

摘要

命令模式(Command Pattern)是行为型设计模式的"解耦专家",它将请求封装为独立对象,使不同请求、队列或日志请求成为可能。本文将深入探讨命令模式的核心思想、实现方式及实际应用场景,通过丰富的Java代码示例展示其如何实现请求方与执行方的解耦,并分析撤销/重做、事务处理等高级应用场景。

一、命令模式核心思想

命令模式的核心是将"请求"封装成对象,通过参数化方式传递这些对象,支持以下关键特性:

  1. 请求封装:将操作封装为包含所有信息的独立对象
  2. 解耦调用者与执行者:调用者不直接调用接收者
  3. 支持操作序列化:命令可存储、传递、排队或记录
  4. 撤销/重做支持:记录历史命令实现逆向操作

应用场景:

  • 需要将操作请求者与执行者解耦时
  • 需要支持事务、撤销、重做功能
  • 需要排队执行或延迟执行请求
  • 需要宏命令(命令组合)

二、命令模式结构解析

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)
    );

总结

命令模式通过将请求封装为对象,实现了请求发起者与执行者的彻底解耦,为解决复杂交互系统提供了强大架构支持。其核心价值体现在三个维度:

  1. 架构灵活性:解耦调用与执行,支持异步、队列、日志等机制
  2. 历史管理能力:内置撤销/重做支持,是实现事务系统的核心模式
  3. 扩展性:宏命令组合、新命令添加不影响现有结构

在实际应用中,命令模式特别适合需要实现操作回滚、命令调度、多级撤销的场景,如金融交易系统、图形编辑软件、智能家居控制等。但同时也要注意避免过度使用:简单场景中使用命令模式可能导致不必要的类膨胀和结构复杂化。

在现代开发中,命令模式正在以新形式演进:函数式编程的结合(Lambda命令)、响应式实现、分布式命令队列等。深刻理解命令模式的核心思想--"封装请求为对象",将帮助开发者构建出更加灵活、健壮的系统架构。