设计模式之:命令模式

文章目录

    • 什么是命令模式?
    • 核心思想
    • 生活中的命令模式
    • 模式结构
    • 基础示例:智能家居系统
      • [1. 命令接口](#1. 命令接口)
      • [2. 接收者 - 家电设备](#2. 接收者 - 家电设备)
      • [3. 具体命令类](#3. 具体命令类)
      • [4. 调用者 - 遥控器](#4. 调用者 - 遥控器)
      • [5. 客户端使用](#5. 客户端使用)
    • 完整示例:文本编辑器
      • [1. 文本编辑器接收者](#1. 文本编辑器接收者)
      • [2. 编辑器命令](#2. 编辑器命令)
      • [3. 编辑器调用者](#3. 编辑器调用者)
      • [4. 文本编辑器客户端](#4. 文本编辑器客户端)
    • 命令模式的优点
      • [1. 解耦调用者和接收者](#1. 解耦调用者和接收者)
      • [2. 支持撤销和重做](#2. 支持撤销和重做)
      • [3. 支持命令队列和日志](#3. 支持命令队列和日志)
    • 命令模式的缺点
      • [1. 可能产生大量命令类](#1. 可能产生大量命令类)
      • [2. 增加系统复杂度](#2. 增加系统复杂度)
    • 适用场景
    • 最佳实践
      • [1. 使用宏命令](#1. 使用宏命令)
      • [2. 实现空对象](#2. 实现空对象)
      • [3. 考虑命令的生命周期](#3. 考虑命令的生命周期)
    • [命令模式 vs 其他模式](#命令模式 vs 其他模式)
    • 总结

什么是命令模式?

命令模式(Command Pattern)是一种行为型设计模式,它将请求封装成一个对象,从而让你可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

核心思想

命令模式的核心思想是:将请求封装成对象,使得可以用不同的请求对客户端进行参数化,并支持请求的排队、记录、撤销等操作。

生活中的命令模式

想象一下餐厅的点餐流程:

  • 顾客(客户端)创建订单(命令对象)
  • 服务员(调用者)接收订单并交给厨房
  • 厨师(接收者)根据订单准备食物
  • 订单可以取消、修改或重复使用

订单就是命令对象!

模式结构

命令模式包含五个核心角色:

  1. 命令接口(Command):声明执行操作的接口
  2. 具体命令(Concrete Command):实现命令接口,绑定接收者和动作
  3. 接收者(Receiver):知道如何执行请求的具体操作
  4. 调用者(Invoker):要求命令执行请求
  5. 客户端(Client):创建具体命令并设置接收者

基础示例:智能家居系统

1. 命令接口

java 复制代码
/**
 * 命令接口 - 声明执行操作的接口
 */
public interface Command {
    /**
     * 执行命令
     */
    void execute();
    
    /**
     * 撤销命令
     */
    void undo();
    
    /**
     * 获取命令描述
     */
    String getDescription();
}

2. 接收者 - 家电设备

java 复制代码
/**
 * 电灯 - 接收者
 * 知道如何执行具体的操作
 */
public class Light {
    private final String location;
    private boolean isOn;
    private int brightness; // 亮度级别 0-100
    
    public Light(String location) {
        this.location = location;
        this.isOn = false;
        this.brightness = 50; // 默认亮度
        System.out.println("💡 初始化电灯: " + location);
    }
    
    public void turnOn() {
        this.isOn = true;
        System.out.println("💡 " + location + " 电灯已打开");
    }
    
    public void turnOff() {
        this.isOn = false;
        System.out.println("💡 " + location + " 电灯已关闭");
    }
    
    public void setBrightness(int level) {
        if (level < 0) level = 0;
        if (level > 100) level = 100;
        this.brightness = level;
        System.out.println("💡 " + location + " 亮度设置为: " + level + "%");
    }
    
    public void dim() {
        setBrightness(brightness - 10);
    }
    
    public void brighten() {
        setBrightness(brightness + 10);
    }
    
    public boolean isOn() {
        return isOn;
    }
    
    public int getBrightness() {
        return brightness;
    }
    
    public String getStatus() {
        return String.format("电灯[%s] - 状态: %s, 亮度: %d%%", 
                           location, isOn ? "开" : "关", brightness);
    }
}

/**
 * 空调 - 接收者
 */
public class AirConditioner {
    private final String location;
    private boolean isOn;
    private int temperature;
    private String mode; // 制冷/制热/送风
    
    public AirConditioner(String location) {
        this.location = location;
        this.isOn = false;
        this.temperature = 26;
        this.mode = "制冷";
        System.out.println("❄️ 初始化空调: " + location);
    }
    
    public void turnOn() {
        this.isOn = true;
        System.out.println("❄️ " + location + " 空调已打开");
    }
    
    public void turnOff() {
        this.isOn = false;
        System.out.println("❄️ " + location + " 空调已关闭");
    }
    
    public void setTemperature(int temperature) {
        this.temperature = temperature;
        System.out.println("❄️ " + location + " 温度设置为: " + temperature + "°C");
    }
    
    public void setMode(String mode) {
        this.mode = mode;
        System.out.println("❄️ " + location + " 模式设置为: " + mode);
    }
    
    public boolean isOn() {
        return isOn;
    }
    
    public String getStatus() {
        return String.format("空调[%s] - 状态: %s, 温度: %d°C, 模式: %s", 
                           location, isOn ? "开" : "关", temperature, mode);
    }
}

/**
 * 电视 - 接收者
 */
public class Television {
    private final String location;
    private boolean isOn;
    private int volume;
    private int channel;
    
    public Television(String location) {
        this.location = location;
        this.isOn = false;
        this.volume = 20;
        this.channel = 1;
        System.out.println("📺 初始化电视: " + location);
    }
    
    public void turnOn() {
        this.isOn = true;
        System.out.println("📺 " + location + " 电视已打开");
    }
    
    public void turnOff() {
        this.isOn = false;
        System.out.println("📺 " + location + " 电视已关闭");
    }
    
    public void setVolume(int volume) {
        this.volume = volume;
        System.out.println("📺 " + location + " 音量设置为: " + volume);
    }
    
    public void setChannel(int channel) {
        this.channel = channel;
        System.out.println("📺 " + location + " 频道切换到: " + channel);
    }
    
    public void volumeUp() {
        setVolume(volume + 5);
    }
    
    public void volumeDown() {
        setVolume(volume - 5);
    }
    
    public void channelUp() {
        setChannel(channel + 1);
    }
    
    public void channelDown() {
        setChannel(channel - 1);
    }
    
    public boolean isOn() {
        return isOn;
    }
    
    public String getStatus() {
        return String.format("电视[%s] - 状态: %s, 频道: %d, 音量: %d", 
                           location, isOn ? "开" : "关", channel, volume);
    }
}

3. 具体命令类

java 复制代码
/**
 * 电灯开关命令 - 具体命令
 */
public class LightOnCommand implements Command {
    private final Light light;
    private int previousBrightness;
    
    public LightOnCommand(Light light) {
        this.light = light;
    }
    
    @Override
    public void execute() {
        previousBrightness = light.getBrightness();
        light.turnOn();
        light.setBrightness(80); // 默认打开时设置合适亮度
    }
    
    @Override
    public void undo() {
        light.turnOff();
        light.setBrightness(previousBrightness);
    }
    
    @Override
    public String getDescription() {
        return "打开电灯: " + light.getStatus();
    }
}

/**
 * 电灯关闭命令
 */
public class LightOffCommand implements Command {
    private final Light light;
    private int previousBrightness;
    private boolean wasOn;
    
    public LightOffCommand(Light light) {
        this.light = light;
    }
    
    @Override
    public void execute() {
        wasOn = light.isOn();
        previousBrightness = light.getBrightness();
        light.turnOff();
    }
    
    @Override
    public void undo() {
        if (wasOn) {
            light.turnOn();
            light.setBrightness(previousBrightness);
        }
    }
    
    @Override
    public String getDescription() {
        return "关闭电灯: " + light.getStatus();
    }
}

/**
 * 电灯调光命令
 */
public class LightDimCommand implements Command {
    private final Light light;
    private int previousBrightness;
    
    public LightDimCommand(Light light) {
        this.light = light;
    }
    
    @Override
    public void execute() {
        previousBrightness = light.getBrightness();
        light.dim();
    }
    
    @Override
    public void undo() {
        light.setBrightness(previousBrightness);
    }
    
    @Override
    public String getDescription() {
        return "调暗电灯: " + light.getStatus();
    }
}

/**
 * 空调开关命令
 */
public class AirConditionerOnCommand implements Command {
    private final AirConditioner airConditioner;
    private int previousTemperature;
    private String previousMode;
    private boolean wasOn;
    
    public AirConditionerOnCommand(AirConditioner airConditioner) {
        this.airConditioner = airConditioner;
    }
    
    @Override
    public void execute() {
        wasOn = airConditioner.isOn();
        previousTemperature = 26; // 简化处理
        previousMode = "制冷";
        
        airConditioner.turnOn();
        airConditioner.setTemperature(24);
        airConditioner.setMode("制冷");
    }
    
    @Override
    public void undo() {
        if (!wasOn) {
            airConditioner.turnOff();
        }
        // 在实际应用中,应该恢复之前的设置
    }
    
    @Override
    public String getDescription() {
        return "打开空调: " + airConditioner.getStatus();
    }
}

/**
 * 电视开关命令
 */
public class TelevisionOnCommand implements Command {
    private final Television television;
    private int previousVolume;
    private int previousChannel;
    private boolean wasOn;
    
    public TelevisionOnCommand(Television television) {
        this.television = television;
    }
    
    @Override
    public void execute() {
        wasOn = television.isOn();
        previousVolume = television.isOn() ? television.getStatus().length() : 20; // 简化
        previousChannel = 1;
        
        television.turnOn();
        television.setChannel(1);
        television.setVolume(25);
    }
    
    @Override
    public void undo() {
        if (!wasOn) {
            television.turnOff();
        }
    }
    
    @Override
    public String getDescription() {
        return "打开电视: " + television.getStatus();
    }
}

/**
 * 宏命令 - 同时执行多个命令
 */
public class MacroCommand implements Command {
    private final List<Command> commands;
    private final String name;
    
    public MacroCommand(String name, Command... commands) {
        this.name = name;
        this.commands = new ArrayList<>(Arrays.asList(commands));
    }
    
    public void addCommand(Command command) {
        commands.add(command);
    }
    
    @Override
    public void execute() {
        System.out.println("🎮 执行宏命令: " + name);
        for (Command command : commands) {
            command.execute();
        }
    }
    
    @Override
    public void undo() {
        System.out.println("↩️ 撤销宏命令: " + name);
        // 按相反顺序撤销
        for (int i = commands.size() - 1; i >= 0; i--) {
            commands.get(i).undo();
        }
    }
    
    @Override
    public String getDescription() {
        return "宏命令[" + name + "] - 包含 " + commands.size() + " 个操作";
    }
}

4. 调用者 - 遥控器

java 复制代码
import java.util.Stack;

/**
 * 智能遥控器 - 调用者
 * 负责触发命令的执行
 */
public class SmartRemoteControl {
    private final Map<String, Command> commandSlots;
    private final Stack<Command> commandHistory;
    private Command lastCommand;
    
    public SmartRemoteControl() {
        this.commandSlots = new HashMap<>();
        this.commandHistory = new Stack<>();
        this.lastCommand = null;
        System.out.println("🎛️ 初始化智能遥控器");
    }
    
    /**
     * 设置命令到指定槽位
     */
    public void setCommand(String slot, Command command) {
        commandSlots.put(slot, command);
        System.out.println("📝 设置命令到槽位 [" + slot + "]: " + command.getDescription());
    }
    
    /**
     * 按下按钮执行命令
     */
    public void pressButton(String slot) {
        Command command = commandSlots.get(slot);
        if (command != null) {
            System.out.println("\n🔘 按下按钮: " + slot);
            command.execute();
            commandHistory.push(command);
            lastCommand = command;
        } else {
            System.out.println("❌ 未找到槽位: " + slot);
        }
    }
    
    /**
     * 撤销上一个命令
     */
    public void pressUndo() {
        if (!commandHistory.isEmpty()) {
            Command command = commandHistory.pop();
            System.out.println("\n↩️ 执行撤销操作");
            command.undo();
        } else {
            System.out.println("❌ 没有可撤销的命令");
        }
    }
    
    /**
     * 显示遥控器状态
     */
    public void displayStatus() {
        System.out.println("\n📋 遥控器状态:");
        System.out.println("-".repeat(50));
        commandSlots.forEach((slot, command) -> {
            System.out.printf("🔘 %-15s -> %s%n", slot, command.getDescription());
        });
        System.out.printf("📜 命令历史: %d 个命令%n", commandHistory.size());
    }
    
    /**
     * 清除命令历史
     */
    public void clearHistory() {
        commandHistory.clear();
        System.out.println("🗑️ 清除命令历史");
    }
}

5. 客户端使用

java 复制代码
/**
 * 智能家居客户端
 */
public class SmartHomeClient {
    public static void main(String[] args) {
        System.out.println("=== 命令模式演示 - 智能家居系统 ===\n");
        
        // 创建接收者 - 家电设备
        Light livingRoomLight = new Light("客厅");
        Light bedroomLight = new Light("卧室");
        AirConditioner livingRoomAC = new AirConditioner("客厅");
        Television livingRoomTV = new Television("客厅");
        
        // 创建命令
        Command livingRoomLightOn = new LightOnCommand(livingRoomLight);
        Command livingRoomLightOff = new LightOffCommand(livingRoomLight);
        Command livingRoomLightDim = new LightDimCommand(livingRoomLight);
        Command livingRoomACOn = new AirConditionerOnCommand(livingRoomAC);
        Command livingRoomTVOn = new TelevisionOnCommand(livingRoomTV);
        
        // 创建宏命令
        MacroCommand eveningMode = new MacroCommand("晚间模式", 
            livingRoomLightOn, livingRoomTVOn);
        MacroCommand leaveHomeMode = new MacroCommand("离家模式", 
            livingRoomLightOff, new LightOffCommand(bedroomLight));
        
        // 创建调用者 - 遥控器
        SmartRemoteControl remote = new SmartRemoteControl();
        
        // 配置遥控器
        remote.setCommand("灯开", livingRoomLightOn);
        remote.setCommand("灯关", livingRoomLightOff);
        remote.setCommand("调光", livingRoomLightDim);
        remote.setCommand("空调", livingRoomACOn);
        remote.setCommand("电视", livingRoomTVOn);
        remote.setCommand("晚间", eveningMode);
        remote.setCommand("离家", leaveHomeMode);
        
        // 显示遥控器状态
        remote.displayStatus();
        
        // 演示1:基础命令执行
        demonstrateBasicCommands(remote);
        
        // 演示2:撤销功能
        demonstrateUndoFeature(remote);
        
        // 演示3:宏命令
        demonstrateMacroCommands(remote);
        
        // 演示4:命令队列
        demonstrateCommandQueue();
    }
    
    /**
     * 演示基础命令执行
     */
    private static void demonstrateBasicCommands(SmartRemoteControl remote) {
        System.out.println("\n1. 基础命令执行演示:");
        System.out.println("=" .repeat(40));
        
        remote.pressButton("灯开");
        remote.pressButton("调光");
        remote.pressButton("电视");
        remote.pressButton("空调");
    }
    
    /**
     * 演示撤销功能
     */
    private static void demonstrateUndoFeature(SmartRemoteControl remote) {
        System.out.println("\n2. 撤销功能演示:");
        System.out.println("=" .repeat(40));
        
        remote.pressButton("灯关");
        remote.pressUndo(); // 应该重新打开灯
        
        remote.pressButton("调光");
        remote.pressButton("调光");
        remote.pressUndo(); // 撤销一次调光
        remote.pressUndo(); // 再撤销一次调光
    }
    
    /**
     * 演示宏命令
     */
    private static void demonstrateMacroCommands(SmartRemoteControl remote) {
        System.out.println("\n3. 宏命令演示:");
        System.out.println("=" .repeat(40));
        
        System.out.println("执行晚间模式:");
        remote.pressButton("晚间");
        
        System.out.println("\n执行离家模式:");
        remote.pressButton("离家");
        
        System.out.println("\n撤销离家模式:");
        remote.pressUndo();
    }
    
    /**
     * 演示命令队列
     */
    private static void demonstrateCommandQueue() {
        System.out.println("\n4. 命令队列演示:");
        System.out.println("=" .repeat(40));
        
        CommandProcessor processor = new CommandProcessor();
        
        Light kitchenLight = new Light("厨房");
        Television kitchenTV = new Television("厨房");
        
        processor.addCommand(new LightOnCommand(kitchenLight));
        processor.addCommand(new TelevisionOnCommand(kitchenTV));
        processor.addCommand(new LightDimCommand(kitchenLight));
        
        System.out.println("处理命令队列...");
        processor.processCommands();
    }
}

/**
 * 命令处理器 - 支持命令队列
 */
class CommandProcessor {
    private final Queue<Command> commandQueue;
    
    public CommandProcessor() {
        this.commandQueue = new LinkedList<>();
    }
    
    public void addCommand(Command command) {
        commandQueue.offer(command);
        System.out.println("📥 添加命令到队列: " + command.getDescription());
    }
    
    public void processCommands() {
        System.out.println("🔄 开始处理命令队列...");
        while (!commandQueue.isEmpty()) {
            Command command = commandQueue.poll();
            System.out.println("⚡ 执行: " + command.getDescription());
            command.execute();
            try {
                Thread.sleep(500); // 模拟处理时间
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        System.out.println("✅ 所有命令处理完成");
    }
}

完整示例:文本编辑器

让我们通过一个更复杂的文本编辑器示例来深入理解命令模式。

1. 文本编辑器接收者

java 复制代码
import java.util.ArrayList;
import java.util.List;

/**
 * 文本编辑器 - 接收者
 * 知道如何执行文本操作
 */
public class TextEditor {
    private StringBuilder content;
    private int cursorPosition;
    private List<String> clipboard;
    
    public TextEditor() {
        this.content = new StringBuilder();
        this.cursorPosition = 0;
        this.clipboard = new ArrayList<>();
        System.out.println("📝 初始化文本编辑器");
    }
    
    public void insertText(String text) {
        content.insert(cursorPosition, text);
        cursorPosition += text.length();
        System.out.println("📝 插入文本: \"" + text + "\"");
        displayContent();
    }
    
    public void deleteText(int length) {
        if (cursorPosition >= length) {
            int start = cursorPosition - length;
            String deleted = content.substring(start, cursorPosition);
            content.delete(start, cursorPosition);
            cursorPosition = start;
            System.out.println("🗑️ 删除文本: \"" + deleted + "\"");
            displayContent();
        }
    }
    
    public void copyText(int start, int end) {
        if (start >= 0 && end <= content.length() && start < end) {
            String copied = content.substring(start, end);
            clipboard.add(copied);
            System.out.println("📋 复制文本: \"" + copied + "\"");
        }
    }
    
    public void pasteText() {
        if (!clipboard.isEmpty()) {
            String textToPaste = clipboard.get(clipboard.size() - 1);
            insertText(textToPaste);
        }
    }
    
    public void setCursorPosition(int position) {
        if (position >= 0 && position <= content.length()) {
            this.cursorPosition = position;
            System.out.println("📍 光标位置设置为: " + position);
        }
    }
    
    public void boldText(int start, int end) {
        if (start >= 0 && end <= content.length() && start < end) {
            content.insert(end, "**");
            content.insert(start, "**");
            cursorPosition = end + 2;
            System.out.println("🔷 加粗文本范围: " + start + "-" + end);
            displayContent();
        }
    }
    
    public void undoInsert(int position, int length) {
        content.delete(position, position + length);
        cursorPosition = position;
        displayContent();
    }
    
    public void undoDelete(int position, String text) {
        content.insert(position, text);
        cursorPosition = position + text.length();
        displayContent();
    }
    
    public String getContent() {
        return content.toString();
    }
    
    public int getCursorPosition() {
        return cursorPosition;
    }
    
    public void displayContent() {
        String display = content.toString();
        if (display.length() > 50) {
            display = display.substring(0, 47) + "...";
        }
        System.out.println("📄 内容: \"" + display + "\" [光标位置: " + cursorPosition + "]");
    }
    
    public void displayFullContent() {
        System.out.println("📄 完整内容: \"" + content.toString() + "\"");
    }
}

2. 编辑器命令

java 复制代码
/**
 * 插入文本命令
 */
public class InsertTextCommand implements Command {
    private final TextEditor editor;
    private final String text;
    private int insertPosition;
    
    public InsertTextCommand(TextEditor editor, String text) {
        this.editor = editor;
        this.text = text;
    }
    
    @Override
    public void execute() {
        insertPosition = editor.getCursorPosition();
        editor.insertText(text);
    }
    
    @Override
    public void undo() {
        editor.undoInsert(insertPosition, text.length());
    }
    
    @Override
    public String getDescription() {
        return "插入文本: \"" + text + "\"";
    }
}

/**
 * 删除文本命令
 */
public class DeleteTextCommand implements Command {
    private final TextEditor editor;
    private final int length;
    private int deletePosition;
    private String deletedText;
    
    public DeleteTextCommand(TextEditor editor, int length) {
        this.editor = editor;
        this.length = length;
    }
    
    @Override
    public void execute() {
        deletePosition = editor.getCursorPosition() - length;
        if (deletePosition >= 0) {
            int end = editor.getCursorPosition();
            deletedText = editor.getContent().substring(deletePosition, end);
            editor.deleteText(length);
        }
    }
    
    @Override
    public void undo() {
        if (deletedText != null) {
            editor.undoDelete(deletePosition, deletedText);
        }
    }
    
    @Override
    public String getDescription() {
        return "删除 " + length + " 个字符";
    }
}

/**
 * 复制文本命令
 */
public class CopyTextCommand implements Command {
    private final TextEditor editor;
    private final int start;
    private final int end;
    
    public CopyTextCommand(TextEditor editor, int start, int end) {
        this.editor = editor;
        this.start = start;
        this.end = end;
    }
    
    @Override
    public void execute() {
        editor.copyText(start, end);
    }
    
    @Override
    public void undo() {
        // 复制操作通常不需要撤销
        System.out.println("⚠️ 复制操作不可撤销");
    }
    
    @Override
    public String getDescription() {
        return "复制文本范围: " + start + "-" + end;
    }
}

/**
 * 粘贴文本命令
 */
public class PasteTextCommand implements Command {
    private final TextEditor editor;
    private int pastePosition;
    private String pastedText;
    
    public PasteTextCommand(TextEditor editor) {
        this.editor = editor;
    }
    
    @Override
    public void execute() {
        pastePosition = editor.getCursorPosition();
        // 这里简化处理,实际应该从剪贴板获取
        pastedText = "已复制的文本";
        editor.pasteText();
    }
    
    @Override
    public void undo() {
        if (pastedText != null) {
            editor.undoInsert(pastePosition, pastedText.length());
        }
    }
    
    @Override
    public String getDescription() {
        return "粘贴文本";
    }
}

/**
 * 加粗文本命令
 */
public class BoldTextCommand implements Command {
    private final TextEditor editor;
    private final int start;
    private final int end;
    
    public BoldTextCommand(TextEditor editor, int start, int end) {
        this.editor = editor;
        this.start = start;
        this.end = end;
    }
    
    @Override
    public void execute() {
        editor.boldText(start, end);
    }
    
    @Override
    public void undo() {
        // 简化处理,实际应该记录更详细的状态
        System.out.println("↩️ 撤销加粗操作");
    }
    
    @Override
    public String getDescription() {
        return "加粗文本: " + start + "-" + end;
    }
}

3. 编辑器调用者

java 复制代码
import java.util.Stack;

/**
 * 文本编辑器控制器 - 调用者
 */
public class EditorController {
    private final TextEditor editor;
    private final Stack<Command> history;
    private final Stack<Command> redoStack;
    
    public EditorController(TextEditor editor) {
        this.editor = editor;
        this.history = new Stack<>();
        this.redoStack = new Stack<>();
        System.out.println("🎮 初始化编辑器控制器");
    }
    
    public void executeCommand(Command command) {
        command.execute();
        history.push(command);
        redoStack.clear(); // 执行新命令时清空重做栈
        System.out.println("✅ 执行: " + command.getDescription());
    }
    
    public void undo() {
        if (!history.isEmpty()) {
            Command command = history.pop();
            command.undo();
            redoStack.push(command);
            System.out.println("↩️ 撤销: " + command.getDescription());
        } else {
            System.out.println("❌ 没有可撤销的操作");
        }
    }
    
    public void redo() {
        if (!redoStack.isEmpty()) {
            Command command = redoStack.pop();
            command.execute();
            history.push(command);
            System.out.println("↪️ 重做: " + command.getDescription());
        } else {
            System.out.println("❌ 没有可重做的操作");
        }
    }
    
    public void showHistory() {
        System.out.println("\n📜 操作历史:");
        System.out.println("-".repeat(40));
        for (int i = 0; i < history.size(); i++) {
            System.out.printf("%2d. %s%n", i + 1, history.get(i).getDescription());
        }
    }
    
    public void showEditorStatus() {
        System.out.println("\n📊 编辑器状态:");
        editor.displayFullContent();
    }
}

4. 文本编辑器客户端

java 复制代码
/**
 * 文本编辑器客户端
 */
public class TextEditorClient {
    public static void main(String[] args) {
        System.out.println("=== 命令模式演示 - 文本编辑器 ===\n");
        
        // 创建接收者
        TextEditor editor = new TextEditor();
        
        // 创建调用者
        EditorController controller = new EditorController(editor);
        
        // 演示1:基础文本操作
        demonstrateBasicOperations(controller, editor);
        
        // 演示2:撤销重做功能
        demonstrateUndoRedo(controller);
        
        // 演示3:复杂操作序列
        demonstrateComplexOperations(controller, editor);
        
        // 显示最终状态
        controller.showEditorStatus();
        controller.showHistory();
    }
    
    private static void demonstrateBasicOperations(EditorController controller, TextEditor editor) {
        System.out.println("1. 基础文本操作演示:");
        System.out.println("=" .repeat(40));
        
        controller.executeCommand(new InsertTextCommand(editor, "Hello, "));
        controller.executeCommand(new InsertTextCommand(editor, "Command Pattern!"));
        controller.executeCommand(new InsertTextCommand(editor, " 这是一个命令模式的演示。"));
        
        // 设置光标位置并删除
        editor.setCursorPosition(7); // 移动到 "Hello, " 后面
        controller.executeCommand(new DeleteTextCommand(editor, 8)); // 删除 "Command"
    }
    
    private static void demonstrateUndoRedo(EditorController controller) {
        System.out.println("\n2. 撤销重做功能演示:");
        System.out.println("=" .repeat(40));
        
        System.out.println("撤销两次操作:");
        controller.undo();
        controller.undo();
        
        System.out.println("\n重做一次操作:");
        controller.redo();
        
        System.out.println("\n再次撤销:");
        controller.undo();
    }
    
    private static void demonstrateComplexOperations(EditorController controller, TextEditor editor) {
        System.out.println("\n3. 复杂操作序列演示:");
        System.out.println("=" .repeat(40));
        
        // 插入新内容
        controller.executeCommand(new InsertTextCommand(editor, "\n\n新段落开始。"));
        controller.executeCommand(new InsertTextCommand(editor, " 这是重要内容。"));
        
        // 复制和粘贴(简化演示)
        editor.setCursorPosition(0);
        controller.executeCommand(new CopyTextCommand(editor, 0, 5));
        controller.executeCommand(new PasteTextCommand(editor));
        
        // 加粗文本
        controller.executeCommand(new BoldTextCommand(editor, 10, 15));
        
        System.out.println("\n执行多次撤销:");
        for (int i = 0; i < 3; i++) {
            controller.undo();
        }
    }
}

命令模式的优点

1. 解耦调用者和接收者

java 复制代码
// 调用者不需要知道接收者的具体细节
// 只需要知道如何触发命令

2. 支持撤销和重做

java 复制代码
// 命令对象可以保存状态
// 很容易实现撤销/重做功能

3. 支持命令队列和日志

java 复制代码
// 可以轻松实现命令队列
// 支持命令的延迟执行和日志记录

命令模式的缺点

1. 可能产生大量命令类

java 复制代码
// 每个操作都需要一个命令类
// 可能导致类的数量增加

2. 增加系统复杂度

java 复制代码
// 引入了额外的抽象层
// 对于简单操作可能过于复杂

适用场景

  1. 需要支持撤销/重做操作时
  2. 需要将操作参数化时
  3. 需要支持命令队列或日志时
  4. 需要支持事务操作时

最佳实践

1. 使用宏命令

java 复制代码
// 将多个命令组合成一个宏命令
// 支持批量操作

2. 实现空对象

java 复制代码
// 实现一个空命令对象
// 避免空指针检查

3. 考虑命令的生命周期

java 复制代码
// 对于需要长时间运行的命令
// 考虑实现取消功能

命令模式 vs 其他模式

模式 目的 特点
命令模式 封装请求 支持撤销、队列、参数化
策略模式 封装算法 算法可以互相替换
责任链模式 处理请求 多个对象可以处理请求

总结

命令模式就像是"任务委托书",把请求打包成对象,让它们可以被传递、存储和执行。

核心价值:

  • 将请求封装成对象
  • 支持撤销和重做操作
  • 支持命令队列和日志
  • 解耦请求发送者和接收者

使用场景:

  • 需要实现撤销/重做功能时
  • 需要将操作参数化时
  • 需要支持事务操作时

简单记忆:

请求太多难管理?命令模式来解决!

打包成对象,撤销重做都容易。

掌握命令模式,能够让你构建出支持复杂操作流程的系统,提供更好的用户体验!

相关推荐
冷崖3 小时前
C++父类与子类进行交互
1024程序员节
野犬寒鸦3 小时前
从零起步学习MySQL || 第十章:深入了解B+树及B+树的性能优势(结合底层数据结构与数据库设计深度解析)
java·数据库·后端·mysql·1024程序员节
Never_Satisfied3 小时前
在JavaScript / HTML中,无法通过开发者工具查看DOM元素中input里输入的密码
1024程序员节
程序猿阿伟3 小时前
《打破数据孤岛:3D手游角色表情骨骼协同的实践指南》
1024程序员节
kura_tsuki3 小时前
[Docker 集群] 私有仓库 + compose
1024程序员节
时间的情敌3 小时前
前端实现大文件上传全流程详解
1024程序员节
冬天的雪20083 小时前
SpringBoot知识点总结
1024程序员节
JoannaJuanCV3 小时前
大模型基础:Rotary Position Embedding(RoPE)
大模型·1024程序员节·qwen3
库库林_沙琪马3 小时前
SpringBoot内容协商机制
1024程序员节