命令模式详解:将请求封装为对象,实现解耦与扩展

2025/10/12 Design Patterns 共 7876 字,约 23 分钟

命令模式详解:将请求封装为对象,实现解耦与扩展

在软件设计中,我们经常遇到需要将请求的发送者与接收者解耦的场景。命令模式(Command Pattern)正是为解决这类问题而生的一种行为型设计模式。它将请求封装成一个独立的对象,使得我们可以参数化客户端的不同请求,队列化或记录请求,并支持可撤销的操作。

什么是命令模式?

命令模式的核心思想是将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

模式结构

命令模式包含以下几个主要角色:

  1. Command(命令接口):声明执行操作的接口
  2. ConcreteCommand(具体命令):将一个接收者对象绑定于一个动作,实现执行方法
  3. Invoker(调用者):要求命令执行请求
  4. Receiver(接收者):知道如何实施与执行一个请求相关的操作
  5. Client(客户端):创建具体命令对象并设置其接收者

命令模式的实现

让我们通过一个实际的例子来理解命令模式的实现。假设我们正在开发一个智能家居控制系统,需要控制各种设备如灯光、空调等。

基础实现

首先,我们定义命令接口:

// 命令接口
public interface Command {
    void execute();
    void undo();
}

接下来,创建接收者类 - 具体的设备:

// 接收者 - 灯光
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 + " 灯光已关闭");
    }
}

// 接收者 - 空调
public class AirConditioner {
    private int temperature = 26;
    
    public void on() {
        System.out.println("空调已打开,当前温度:" + temperature);
    }
    
    public void off() {
        System.out.println("空调已关闭");
    }
    
    public void setTemperature(int temperature) {
        this.temperature = temperature;
        System.out.println("空调温度设置为:" + temperature);
    }
    
    public int getTemperature() {
        return temperature;
    }
}

然后,实现具体命令类:

// 具体命令 - 灯光开关命令
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();
    }
}

public 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();
    }
}

// 具体命令 - 空调温度设置命令
public class SetTemperatureCommand implements Command {
    private AirConditioner airConditioner;
    private int newTemperature;
    private int previousTemperature;
    
    public SetTemperatureCommand(AirConditioner airConditioner, int temperature) {
        this.airConditioner = airConditioner;
        this.newTemperature = temperature;
        this.previousTemperature = airConditioner.getTemperature();
    }
    
    @Override
    public void execute() {
        previousTemperature = airConditioner.getTemperature();
        airConditioner.setTemperature(newTemperature);
    }
    
    @Override
    public void undo() {
        airConditioner.setTemperature(previousTemperature);
    }
}

创建调用者类:

// 调用者 - 遥控器
public class RemoteControl {
    private Command command;
    private Command lastCommand;
    
    public void setCommand(Command command) {
        this.command = command;
    }
    
    public void pressButton() {
        command.execute();
        lastCommand = command;
    }
    
    public void pressUndo() {
        if (lastCommand != null) {
            lastCommand.undo();
            lastCommand = null;
        }
    }
}

最后,客户端使用:

public class SmartHomeClient {
    public static void main(String[] args) {
        // 创建接收者
        Light livingRoomLight = new Light("客厅");
        AirConditioner bedroomAC = new AirConditioner();
        
        // 创建命令
        Command lightOn = new LightOnCommand(livingRoomLight);
        Command lightOff = new LightOffCommand(livingRoomLight);
        Command setTemp = new SetTemperatureCommand(bedroomAC, 22);
        
        // 创建调用者
        RemoteControl remote = new RemoteControl();
        
        // 执行命令
        remote.setCommand(lightOn);
        remote.pressButton(); // 打开客厅灯光
        
        remote.setCommand(setTemp);
        remote.pressButton(); // 设置空调温度
        
        remote.setCommand(lightOff);
        remote.pressButton(); // 关闭客厅灯光
        
        remote.pressUndo(); // 撤销上一个命令(重新打开灯光)
    }
}

高级功能实现

命令模式真正强大的地方在于它支持复杂的功能,如命令队列、宏命令等。

命令队列和日志

// 支持命令队列和日志的调用者
public class AdvancedRemoteControl {
    private List<Command> commandQueue = new ArrayList<>();
    private List<Command> commandHistory = new ArrayList<>();
    private int currentHistoryIndex = -1;
    
    public void addToQueue(Command command) {
        commandQueue.add(command);
    }
    
    public void executeQueue() {
        for (Command command : commandQueue) {
            command.execute();
            commandHistory.add(command);
            currentHistoryIndex++;
        }
        commandQueue.clear();
    }
    
    public void undoLast() {
        if (currentHistoryIndex >= 0) {
            Command command = commandHistory.get(currentHistoryIndex);
            command.undo();
            currentHistoryIndex--;
        }
    }
    
    public void redo() {
        if (currentHistoryIndex < commandHistory.size() - 1) {
            currentHistoryIndex++;
            Command command = commandHistory.get(currentHistoryIndex);
            command.execute();
        }
    }
}

宏命令

// 宏命令 - 一次性执行多个命令
public class MacroCommand implements Command {
    private List<Command> commands;
    
    public MacroCommand(List<Command> commands) {
        this.commands = commands;
    }
    
    @Override
    public void execute() {
        for (Command command : commands) {
            command.execute();
        }
    }
    
    @Override
    public void undo() {
        // 逆序执行撤销操作
        for (int i = commands.size() - 1; i >= 0; i--) {
            commands.get(i).undo();
        }
    }
}

// 使用宏命令
public class MacroCommandDemo {
    public static void main(String[] args) {
        Light livingRoomLight = new Light("客厅");
        Light bedroomLight = new Light("卧室");
        AirConditioner ac = new AirConditioner();
        
        Command lightOn1 = new LightOnCommand(livingRoomLight);
        Command lightOn2 = new LightOnCommand(bedroomLight);
        Command setTemp = new SetTemperatureCommand(ac, 22);
        
        List<Command> eveningCommands = Arrays.asList(lightOn1, lightOn2, setTemp);
        MacroCommand eveningMode = new MacroCommand(eveningCommands);
        
        RemoteControl remote = new RemoteControl();
        remote.setCommand(eveningMode);
        remote.pressButton(); // 一键执行所有命令
    }
}

实际应用场景

命令模式在现实世界中有广泛的应用:

1. GUI 按钮和菜单操作

在图形用户界面中,按钮点击、菜单选择等操作都可以使用命令模式实现:

// GUI 应用示例
public class GUIApplication {
    public static void main(String[] args) {
        JFrame frame = new JFrame("命令模式示例");
        JButton button = new JButton("执行命令");
        
        // 创建命令
        Document document = new Document();
        Command saveCommand = new SaveCommand(document);
        Command printCommand = new PrintCommand(document);
        
        // 按钮绑定命令
        button.addActionListener(e -> saveCommand.execute());
        
        frame.add(button);
        frame.pack();
        frame.setVisible(true);
    }
}

class Document {
    public void save() {
        System.out.println("文档已保存");
    }
    
    public void print() {
        System.out.println("文档已打印");
    }
}

class SaveCommand implements Command {
    private Document document;
    
    public SaveCommand(Document document) {
        this.document = document;
    }
    
    @Override
    public void execute() {
        document.save();
    }
    
    @Override
    public void undo() {
        System.out.println("无法撤销保存操作");
    }
}

2. 事务系统

在数据库事务中,命令模式可以用于实现事务的原子性:

// 数据库事务命令
public interface TransactionCommand extends Command {
    boolean canExecute();
}

public class TransferCommand implements TransactionCommand {
    private Account fromAccount;
    private Account toAccount;
    private BigDecimal amount;
    private boolean executed = false;
    
    public TransferCommand(Account from, Account to, BigDecimal amount) {
        this.fromAccount = from;
        this.toAccount = to;
        this.amount = amount;
    }
    
    @Override
    public boolean canExecute() {
        return fromAccount.getBalance().compareTo(amount) >= 0;
    }
    
    @Override
    public void execute() {
        if (canExecute()) {
            fromAccount.withdraw(amount);
            toAccount.deposit(amount);
            executed = true;
            System.out.println("转账成功:" + amount);
        }
    }
    
    @Override
    public void undo() {
        if (executed) {
            toAccount.withdraw(amount);
            fromAccount.deposit(amount);
            System.out.println("撤销转账:" + amount);
        }
    }
}

3. 任务调度系统

// 任务调度器
public class TaskScheduler {
    private PriorityQueue<ScheduledTask> taskQueue = new PriorityQueue<>();
    
    public void scheduleTask(Command task, long delay) {
        ScheduledTask scheduledTask = new ScheduledTask(task, 
            System.currentTimeMillis() + delay);
        taskQueue.offer(scheduledTask);
    }
    
    public void processTasks() {
        long currentTime = System.currentTimeMillis();
        while (!taskQueue.isEmpty() && 
               taskQueue.peek().getExecuteTime() <= currentTime) {
            ScheduledTask task = taskQueue.poll();
            task.getCommand().execute();
        }
    }
    
    private static class ScheduledTask implements Comparable<ScheduledTask> {
        private Command command;
        private long executeTime;
        
        public ScheduledTask(Command command, long executeTime) {
            this.command = command;
            this.executeTime = executeTime;
        }
        
        @Override
        public int compareTo(ScheduledTask other) {
            return Long.compare(this.executeTime, other.executeTime);
        }
        
        public Command getCommand() { return command; }
        public long getExecuteTime() { return executeTime; }
    }
}

命令模式的优缺点

优点

  1. 解耦调用者和接收者:调用者不需要知道接收者的具体实现细节
  2. 支持撤销和重做:可以轻松实现命令的撤销和重做功能
  3. 支持命令队列:可以将命令放入队列中,延迟执行或批量执行
  4. 易于扩展:新增命令不会影响现有代码
  5. 支持宏命令:可以组合多个命令形成复杂的操作

缺点

  1. 可能产生大量具体命令类:每个操作都需要一个具体的命令类
  2. 增加了系统复杂性:引入了多个新的类和接口

最佳实践

  1. 使用Lambda表达式简化:在Java 8+中,可以使用函数式接口简化命令模式的实现
  2. 考虑命令的生命周期:对于需要长时间运行的命令,考虑实现进度跟踪和取消功能
  3. 命令序列化:如果需要持久化命令,确保命令对象是可序列化的
  4. 错误处理:在命令执行过程中妥善处理异常情况
// 使用Lambda简化命令模式
public class LambdaCommandDemo {
    public static void main(String[] args) {
        RemoteControl remote = new RemoteControl();
        
        // 使用Lambda表达式创建命令
        remote.setCommand(() -> System.out.println("简单命令执行"));
        remote.pressButton();
        
        // 使用方法引用
        Light light = new Light("书房");
        remote.setCommand(light::on);
        remote.pressButton();
    }
}

总结

命令模式通过将请求封装为对象,实现了调用者与接收者的解耦,为软件设计带来了极大的灵活性。它不仅支持基本的命令执行,还能够轻松实现撤销、重做、命令队列、宏命令等高级功能。在实际开发中,合理运用命令模式可以显著提高代码的可维护性和扩展性。

虽然命令模式可能会增加一些类的数量,但其带来的设计优势在复杂的业务场景中往往是值得的。特别是在需要支持操作历史、事务处理、任务调度等功能的系统中,命令模式提供了一个优雅而强大的解决方案。

文档信息

Search

    Table of Contents