模式定义

命令模式是一种行为型设计模式,它将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化。命令模式将请求的发送者和接收者解耦,使得系统可以将请求作为对象存储、传递和回调。它也支持撤销操作和队列请求等功能。

模式结构

graph TD A[Client] --> B((Invoker)) B --> C[Command] C --> D[Receiver]

实现方式

基础实现

定义命令接口,实现具体命令类、接收者和调用者。

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

// 接收者 - 灯
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 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 RemoteControl {
    private Command[] onCommands;
    private Command[] offCommands;
    private Command undoCommand;
    
    public RemoteControl() {
        onCommands = new Command[7];
        offCommands = new Command[7];
        
        Command noCommand = new Command() {
            public void execute() {}
            public void undo() {}
        };
        
        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;
    }
    
    public void onButtonWasPushed(int slot) {
        onCommands[slot].execute();
        undoCommand = onCommands[slot];
    }
    
    public void offButtonWasPushed(int slot) {
        offCommands[slot].execute();
        undoCommand = offCommands[slot];
    }
    
    public void undoButtonWasPushed() {
        undoCommand.undo();
    }
}

使用示例

public class CommandPatternDemo {
    public static void main(String[] args) {
        RemoteControl remoteControl = new RemoteControl();
        
        Light livingRoomLight = new Light("客厅");
        Light kitchenLight = new Light("厨房");
        
        LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
        LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);
        LightOnCommand kitchenLightOn = new LightOnCommand(kitchenLight);
        LightOffCommand kitchenLightOff = new LightOffCommand(kitchenLight);
        
        remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
        remoteControl.setCommand(1, kitchenLightOn, kitchenLightOff);
        
        System.out.println(remoteControl);
        
        remoteControl.onButtonWasPushed(0);
        remoteControl.offButtonWasPushed(0);
        remoteControl.onButtonWasPushed(1);
        remoteControl.offButtonWasPushed(1);
        remoteControl.undoButtonWasPushed();
    }
}

经典案例

1. Swing GUI中的Action接口

Java Swing框架广泛使用命令模式。javax.swing.Action接口就是命令模式的一个典型实现:

Action saveAction = new AbstractAction("Save") {
    public void actionPerformed(ActionEvent e) {
        // 保存文档逻辑
        document.save();
    }
};

JButton saveButton = new JButton(saveAction);
JMenuItem saveMenuItem = new JMenuItem(saveAction);

2. Runnable接口

Java中的Runnable接口也是命令模式的一种体现,线程执行的任务被封装在Runnable对象中:

Runnable task = new Runnable() {
    public void run() {
        // 执行任务
        System.out.println("Task executed");
    }
};

Thread thread = new Thread(task);
thread.start();

3. 日志和恢复系统

命令模式可以与备忘录模式结合,实现操作的日志记录和系统恢复功能:

interface Command {
    void execute();
    void store(); // 存储状态用于恢复
    void load();  // 加载之前存储的状态
}

应用场景

优缺点

优点

  • 降低耦合度:发送者与接收者之间没有直接引用关系
  • 扩展性好:很容易增加新的命令类
  • 支持撤销操作:通过undo方法实现命令撤销
  • 支持队列请求:可以将命令对象放入队列中延迟执行
  • 支持宏命令:可以将多个命令组合成一个复合命令

缺点

  • 类数量增加:每增加一个命令就需要增加一个具体命令类
  • 系统复杂性增加:引入命令模式会增加系统的复杂性和理解难度