如何设计一个支持撤销操作的命令模式框架?

来源:编程学习作者:老毕头衔:草根站长
导读:本期聚焦于小伙伴创作的《如何设计一个支持撤销操作的命令模式框架?》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何设计一个支持撤销操作的命令模式框架?》有用,将其分享出去将是对创作者最好的鼓励。

命令模式通过将请求封装为独立的命令对象,解耦了请求发起者和请求执行者,而支持撤销操作的命令模式框架则是在此基础上增加了操作历史记录与反向执行能力,让系统可以回溯之前的操作状态。这种设计在文本编辑器、图形绘制工具、表单操作等场景中非常实用,用户可以通过撤销、重做操作修正误操作,提升使用体验。

如何设计一个支持撤销操作的命令模式框架?

核心角色定义

一个支持撤销操作的命令模式框架需要包含以下几个核心角色,每个角色各司其职,共同支撑框架的完整功能:

  • 命令接口(Command):定义所有命令的统一执行与撤销方法,是框架的基础契约。
  • 具体命令(ConcreteCommand):实现命令接口,绑定接收者对象,封装具体的执行逻辑与撤销逻辑。
  • 接收者(Receiver):真正执行命令逻辑的对象,包含具体的业务操作方法。
  • 调用者(Invoker):负责触发命令的执行,同时维护操作历史栈,管理撤销与重做操作。
  • 客户端(Client):创建具体命令对象,设置命令的接收者,将命令交给调用者执行。

基础接口设计

首先定义命令接口,所有具体命令都需要实现该接口的两个核心方法:execute 用于执行命令,undo 用于撤销命令。

// 命令接口定义
public interface Command {
    // 执行命令
    void execute();
    // 撤销命令
    void undo();
}

接下来定义接收者角色,以文本编辑器为例,接收者负责维护文本内容,提供文本追加和删除的方法:

// 文本编辑接收者
public class TextEditor {
    private StringBuilder content = new StringBuilder();

    // 追加文本
    public void appendText(String text) {
        content.append(text);
    }

    // 删除末尾指定长度的文本
    public void deleteText(int length) {
        if (length <= content.length()) {
            content.delete(content.length() - length, content.length());
        }
    }

    // 获取当前文本内容
    public String getContent() {
        return content.toString();
    }
}

具体命令实现

具体命令需要绑定对应的接收者,实现执行和撤销的具体逻辑。以文本追加命令为例,执行时调用接收者的追加方法,撤销时调用接收者的删除方法,删除长度就是本次追加的文本长度。

// 文本追加命令
public class AppendTextCommand implements Command {
    private TextEditor editor;
    private String appendedText;

    public AppendTextCommand(TextEditor editor, String appendedText) {
        this.editor = editor;
        this.appendedText = appendedText;
    }

    @Override
    public void execute() {
        editor.appendText(appendedText);
    }

    @Override
    public void undo() {
        editor.deleteText(appendedText.length());
    }
}

调用者与历史管理

调用者需要维护两个栈:一个用于存放已执行命令的撤销栈,一个用于存放已撤销命令的重做栈。执行命令时将命令压入撤销栈,清空重做栈;撤销命令时从撤销栈弹出命令执行撤销,同时压入重做栈;重做命令时从重做栈弹出命令执行,同时压入撤销栈。

import java.util.Stack;

// 命令调用者,管理操作历史
public class CommandInvoker {
    // 撤销栈,存放已执行的命令
    private Stack<Command> undoStack = new Stack<>();
    // 重做栈,存放已撤销的命令
    private Stack<Command> redoStack = new Stack<>();

    // 执行命令
    public void executeCommand(Command command) {
        command.execute();
        undoStack.push(command);
        // 执行新命令后清空重做栈
        redoStack.clear();
    }

    // 撤销操作
    public void undo() {
        if (!undoStack.isEmpty()) {
            Command command = undoStack.pop();
            command.undo();
            redoStack.push(command);
        }
    }

    // 重做操作
    public void redo() {
        if (!redoStack.isEmpty()) {
            Command command = redoStack.pop();
            command.execute();
            undoStack.push(command);
        }
    }

    // 判断是否可以撤销
    public boolean canUndo() {
        return !undoStack.isEmpty();
    }

    // 判断是否可以重做
    public boolean canRedo() {
        return !redoStack.isEmpty();
    }
}

框架使用示例

客户端只需要创建接收者、具体命令,将命令交给调用者执行即可,无需关心命令的具体执行细节和历史管理逻辑。

public class Client {
    public static void main(String[] args) {
        // 创建接收者
        TextEditor editor = new TextEditor();
        // 创建调用者
        CommandInvoker invoker = new CommandInvoker();

        // 执行追加命令
        Command append1 = new AppendTextCommand(editor, "Hello ");
        invoker.executeCommand(append1);
        System.out.println("第一次追加后内容:" + editor.getContent());

        Command append2 = new AppendTextCommand(editor, "World");
        invoker.executeCommand(append2);
        System.out.println("第二次追加后内容:" + editor.getContent());

        // 撤销操作
        if (invoker.canUndo()) {
            invoker.undo();
            System.out.println("第一次撤销后内容:" + editor.getContent());
        }

        // 重做操作
        if (invoker.canRedo()) {
            invoker.redo();
            System.out.println("第一次重做后内容:" + editor.getContent());
        }

        // 再次撤销
        if (invoker.canUndo()) {
            invoker.undo();
            System.out.println("第二次撤销后内容:" + editor.getContent());
        }
    }
}

框架扩展方向

上述基础框架可以根据实际需求进行扩展:

  • 增加命令的合并能力,比如连续的文本输入可以合并为一个命令,减少撤销栈的存储压力。
  • 支持命令的持久化,将操作历史保存到本地文件或数据库,实现跨会话的撤销重做。
  • 增加命令的执行状态校验,避免重复执行已完成的命令。
  • 支持批量命令,将多个小命令封装为一个组合命令,执行和撤销时批量处理。
设计支持撤销操作的命令模式框架时,核心是保证每个命令的撤销逻辑与执行逻辑完全可逆,同时历史管理模块要清晰维护操作顺序,避免出现状态不一致的问题。

命令模式撤销操作设计模式框架设计修改时间:2026-07-02 20:24:35

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。