命令模式

简介

定义: 将一个请求封装为一个对象,从而可以用不同的请求对客户进行参数化。将请求封装成对象,将动作请求者和动作执行者解耦。

使用场景:

  • GUI按钮和菜单

  • 撤销/重做功能

  • 事务系统

  • 线程池(Runnable)

实现要点:

  • 命令对象持有接收者引用

  • 支持撤销操作

  • 支持宏命令

UML

代码示例

  1. 一些电器

//空调
public class AirConditioner {
    
    private String location;
    private boolean isOn = false;
    private int temperature = 26;
    
    public AirConditioner(String location) {
        this.location = location;
    }
    
    public void on() {
        isOn = true;
        System.out.println("❄️ " + location + "的空调打开了 (温度: " + temperature + "°C)");
    }
    
    public void off() {
        isOn = false;
        System.out.println("❄️ " + location + "的空调关闭了");
    }
    
    public void setTemperature(int temperature) {
        this.temperature = temperature;
        System.out.println("❄️ " + location + "的空调温度设置为: " + temperature + "°C");
    }
    
    public boolean isOn() {
        return isOn;
    }
    
    public int getTemperature() {
        return temperature;
    }
}
//电灯
public class Light {
    
    private String location;
    private int brightness = 0;  // 亮度 0-100
    
    public Light(String location) {
        this.location = location;
    }
    
    public void on() {
        brightness = 100;
        System.out.println("💡 " + location + "的灯打开了 (亮度: " + brightness + "%)");
    }
    
    public void off() {
        brightness = 0;
        System.out.println("💡 " + location + "的灯关闭了");
    }
    
    public void dim(int level) {
        brightness = level;
        System.out.println("💡 " + location + "的灯调暗至: " + level + "%");
    }
    
    public int getBrightness() {
        return brightness;
    }
    
    public String getLocation() {
        return location;
    }
}
//电视
public class TV {
    
    private String location;
    private boolean isOn = false;
    private int channel = 1;
    private int volume = 10;
    
    public TV(String location) {
        this.location = location;
    }
    
    public void on() {
        isOn = true;
        System.out.println("📺 " + location + "的电视打开了 (频道: " + channel + ", 音量: " + volume + ")");
    }
    
    public void off() {
        isOn = false;
        System.out.println("📺 " + location + "的电视关闭了");
    }
    
    public void setChannel(int channel) {
        this.channel = channel;
        System.out.println("📺 " + location + "的电视切换到频道: " + channel);
    }
    
    public void setVolume(int volume) {
        this.volume = volume;
        System.out.println("📺 " + location + "的电视音量设置为: " + volume);
    }
    
    public boolean isOn() {
        return isOn;
    }
    
    public int getChannel() {
        return channel;
    }
    
    public int getVolume() {
        return volume;
    }
}
  1. 命令接口

public interface Command {
    
    /**
     * 执行命令
     */
    void execute();
    
    /**
     * 撤销命令
     */
    void undo();
    
    /**
     * 获取命令描述
     */
    String getDescription();
}
  1. 命令的实现

//开空调命令 - 具体命令类
public 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();
    }
    
    @Override
    public String getDescription() {
        return "打开空调并设置温度";
    }
}
//关灯命令 - 具体命令类
public class LightOffCommand implements Command {
    
    private Light light;
    private int previousBrightness;
    
    public LightOffCommand(Light light) {
        this.light = light;
    }
    
    @Override
    public void execute() {
        previousBrightness = light.getBrightness();
        light.off();
    }
    
    @Override
    public void undo() {
        if (previousBrightness > 0) {
            light.dim(previousBrightness);
        }
    }
    
    @Override
    public String getDescription() {
        return "关闭" + light.getLocation() + "的灯";
    }
}
//开灯命令 - 具体命令类
public class LightOnCommand implements Command {
    
    private Light light;
    private int previousBrightness;
    
    public LightOnCommand(Light light) {
        this.light = light;
    }
    
    @Override
    public void execute() {
        previousBrightness = light.getBrightness();
        light.on();
    }
    
    @Override
    public void undo() {
        if (previousBrightness == 0) {
            light.off();
        } else {
            light.dim(previousBrightness);
        }
    }
    
    @Override
    public String getDescription() {
        return "打开" + light.getLocation() + "的灯";
    }
}
//宏命令 - 批量执行多个命令
public class MacroCommand implements Command {
    
    private Command[] commands;
    private String description;
    
    public MacroCommand(Command[] commands, String description) {
        this.commands = commands;
        this.description = description;
    }
    
    @Override
    public void execute() {
        System.out.println("🎯 执行宏命令: " + description);
        for (Command command : commands) {
            command.execute();
        }
    }
    
    @Override
    public void undo() {
        System.out.println("↩️ 撤销宏命令: " + description);
        // 倒序撤销
        for (int i = commands.length - 1; i >= 0; i--) {
            commands[i].undo();
        }
    }
    
    @Override
    public String getDescription() {
        return description;
    }
}
//空命令 - 用于初始化,避免空指针
public class NoCommand implements Command {
    
    @Override
    public void execute() {
        // 什么都不做
    }
    
    @Override
    public void undo() {
        // 什么都不做
    }
    
    @Override
    public String getDescription() {
        return "空命令";
    }
}
//关电视命令 - 具体命令类
public class TVOffCommand implements Command {
    
    private TV tv;
    
    public TVOffCommand(TV tv) {
        this.tv = tv;
    }
    
    @Override
    public void execute() {
        tv.off();
    }
    
    @Override
    public void undo() {
        tv.on();
    }
    
    @Override
    public String getDescription() {
        return "关闭电视";
    }
}
//开电视命令 - 具体命令类
public class TVOnCommand implements Command {
    
    private TV tv;
    
    public TVOnCommand(TV tv) {
        this.tv = tv;
    }
    
    @Override
    public void execute() {
        tv.on();
    }
    
    @Override
    public void undo() {
        tv.off();
    }
    
    @Override
    public String getDescription() {
        return "打开电视";
    }
}
  1. 命令控制器

public class RemoteControl {
    
    /**
     * 遥控器有7个插槽,每个插槽可以设置一个命令
     */
    private Command[] onCommands;
    private Command[] offCommands;
    
    /**
     * 撤销命令栈
     */
    private Stack<Command> undoStack;
    
    public RemoteControl() {
        onCommands = new Command[7];
        offCommands = new Command[7];
        undoStack = new Stack<>();
        
        // 初始化为空命令,避免空指针
        Command noCommand = new NoCommand();
        for (int i = 0; i < 7; i++) {
            onCommands[i] = noCommand;
            offCommands[i] = noCommand;
        }
    }
    
    /**
     * 设置命令
     */
    public void setCommand(int slot, Command onCommand, Command offCommand) {
        if (slot >= 0 && slot < 7) {
            onCommands[slot] = onCommand;
            offCommands[slot] = offCommand;
        }
    }
    
    /**
     * 按下ON按钮
     */
    public void onButtonWasPressed(int slot) {
        if (slot >= 0 && slot < 7) {
            System.out.println("\n🔘 按下ON按钮 [插槽" + slot + "]");
            onCommands[slot].execute();
            undoStack.push(onCommands[slot]);
        }
    }
    
    /**
     * 按下OFF按钮
     */
    public void offButtonWasPressed(int slot) {
        if (slot >= 0 && slot < 7) {
            System.out.println("\n🔘 按下OFF按钮 [插槽" + slot + "]");
            offCommands[slot].execute();
            undoStack.push(offCommands[slot]);
        }
    }
    
    /**
     * 按下撤销按钮
     */
    public void undoButtonWasPressed() {
        if (!undoStack.isEmpty()) {
            System.out.println("\n↩️ 按下撤销按钮");
            Command command = undoStack.pop();
            command.undo();
        } else {
            System.out.println("\n❌ 没有可撤销的命令");
        }
    }
    
    /**
     * 显示遥控器状态
     */
    public void showStatus() {
        System.out.println("\n📱 ===== 遥控器状态 =====");
        for (int i = 0; i < 7; i++) {
            System.out.println("[插槽" + i + "] ON: " + onCommands[i].getDescription() + 
                             "  |  OFF: " + offCommands[i].getDescription());
        }
        System.out.println("========================");
    }
}

使用方式

public class Test {

    public static void main(String[] args) {
        System.out.println("=== 命令模式 - 智能家居遥控器示例 ===\n");

        // ========== 创建接收者(家电设备) ==========
        Light livingRoomLight = new Light("客厅");
        Light bedroomLight = new Light("卧室");
        TV livingRoomTV = new TV("客厅");
        AirConditioner livingRoomAC = new AirConditioner("客厅");

        // ========== 创建命令对象 ==========
        // 灯光命令
        LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
        LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);
        LightOnCommand bedroomLightOn = new LightOnCommand(bedroomLight);
        LightOffCommand bedroomLightOff = new LightOffCommand(bedroomLight);

        // 电视命令
        TVOnCommand tvOn = new TVOnCommand(livingRoomTV);
        TVOffCommand tvOff = new TVOffCommand(livingRoomTV);

        // 空调命令
        AirConditionerOnCommand acOn = new AirConditionerOnCommand(livingRoomAC);

        // ========== 创建遥控器(调用者) ==========
        RemoteControl remote = new RemoteControl();

        // 设置命令到遥控器插槽
        remote.setCommand(0, livingRoomLightOn, livingRoomLightOff);
        remote.setCommand(1, bedroomLightOn, bedroomLightOff);
        remote.setCommand(2, tvOn, tvOff);
        remote.setCommand(3, acOn, acOn);  // 空调使用同一个命令

        // 显示遥控器状态
        remote.showStatus();

        // ========== 场景1:基本命令执行 ==========
        System.out.println("\n【场景1:基本命令执行】");
        
        // 打开客厅灯
        remote.onButtonWasPressed(0);
        
        // 关闭客厅灯
        remote.offButtonWasPressed(0);
        
        // 打开卧室灯
        remote.onButtonWasPressed(1);
        
        // 打开电视
        remote.onButtonWasPressed(2);

        // ========== 场景2:撤销命令 ==========
        System.out.println("\n\n【场景2:撤销命令】");
        
        // 打开空调
        remote.onButtonWasPressed(3);
        
        // 撤销(关闭空调)
        remote.undoButtonWasPressed();
        
        // 再次撤销(打开电视被撤销,即关闭电视)
        remote.undoButtonWasPressed();

        // ========== 场景3:宏命令(批量执行) ==========
        System.out.println("\n\n【场景3:宏命令 - 回家模式】");
        
        // 创建"回家模式"宏命令:打开客厅灯、打开电视、打开空调
        Command[] partyOnCommands = {
            livingRoomLightOn,
            tvOn,
            acOn
        };
        MacroCommand partyOnMacro = new MacroCommand(partyOnCommands, "回家模式");
        
        // 创建"离家模式"宏命令:关闭所有设备
        Command[] partyOffCommands = {
            livingRoomLightOff,
            bedroomLightOff,
            tvOff
        };
        MacroCommand partyOffMacro = new MacroCommand(partyOffCommands, "离家模式");
        
        // 将宏命令设置到遥控器
        remote.setCommand(6, partyOnMacro, partyOffMacro);
        
        // 执行"回家模式"
        remote.onButtonWasPressed(6);
        
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        // 执行"离家模式"
        System.out.println();
        remote.offButtonWasPressed(6);
        
        // 撤销"离家模式"
        System.out.println();
        remote.undoButtonWasPressed();

        // ========== 总结 ==========
        System.out.println("\n\n=== 命令模式说明 ===");
        System.out.println("1. 命令接口: Command - 定义执行和撤销方法");
        System.out.println("2. 具体命令: LightOnCommand等 - 封装请求为对象");
        System.out.println("3. 接收者: Light、TV等 - 真正执行操作的对象");
        System.out.println("4. 调用者: RemoteControl - 持有命令并触发执行");
        System.out.println("5. 客户端: 创建命令对象并设置接收者");

        System.out.println("\n=== 命令模式优势 ===");
        System.out.println("✅ 解耦: 调用者与接收者解耦");
        System.out.println("✅ 可扩展: 易于添加新命令");
        System.out.println("✅ 可撤销: 支持undo/redo操作");
        System.out.println("✅ 可组合: 支持宏命令(组合多个命令)");
        System.out.println("✅ 可记录: 可以记录命令历史");
        System.out.println("✅ 可队列: 可以将命令放入队列执行");

        System.out.println("\n=== 命令模式应用场景 ===");
        System.out.println("📌 GUI按钮/菜单: 将用户操作封装为命令");
        System.out.println("📌 事务系统: 支持回滚的事务操作");
        System.out.println("📌 宏命令: 批量执行多个操作");
        System.out.println("📌 任务调度: 将任务封装为命令放入队列");
        System.out.println("📌 日志系统: 记录操作历史");
        System.out.println("📌 撤销/重做: 编辑器、绘图软件等");
        System.out.println("📌 线程池: 将任务封装为Runnable命令");

        System.out.println("\n=== 命令模式的关键点 ===");
        System.out.println("🔑 将请求封装为对象");
        System.out.println("🔑 命令对象持有接收者引用");
        System.out.println("🔑 调用者只知道命令接口,不知道具体实现");
        System.out.println("🔑 支持撤销操作(保存状态)");
        System.out.println("🔑 支持宏命令(命令的组合)");

        System.out.println("\n=== 命令模式在Java中的应用 ===");
        System.out.println("🔸 Runnable接口: 将任务封装为命令");
        System.out.println("🔸 Swing/JavaFX: ActionListener封装按钮动作");
        System.out.println("🔸 Spring: ApplicationEvent命令模式应用");
        System.out.println("🔸 数据库事务: 可回滚的操作");
    }
}

Last updated

Was this helpful?