You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java撤销功能实现咨询:如何存储变更前后值并支持撤销

实现Java变更操作的撤销功能思路

嘿,刚接触Java就考虑到做撤销功能,这个意识真的不错!我给你分享几个实用的实现思路,尤其是针对你提到的setName这类方法的场景~

核心思路:命令模式(Command Pattern)

这是实现撤销/重做功能最经典的方案,核心是把每一次变更操作封装成一个独立的命令对象,每个命令里保存执行和撤销需要的所有信息,然后用栈来记录操作历史——毕竟撤销是“倒序”回滚,栈的“后进先出”特性刚好匹配这个需求。

具体步骤(以setName为例)

  • 定义命令接口:统一所有变更命令的执行和撤销方法
  • 实现具体命令类:针对setName编写专门的命令,保存变更前后的名称和目标产品对象
  • 维护操作历史栈:用栈存储执行过的命令,撤销时从栈顶取出命令执行撤销逻辑

代码示例

1. 基础产品类

public class Product {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        // 这里可以保留你原来的null/空字符串处理逻辑
        this.name = name;
    }
}

2. 命令接口

public interface Command {
    // 执行当前变更操作
    void execute();
    // 撤销当前变更操作
    void undo();
}

3. 针对setName的具体命令

public class SetNameCommand implements Command {
    private Product targetProduct;
    private String oldName; // 变更前的旧名称
    private String newName; // 要设置的新名称

    public SetNameCommand(Product product, String newName) {
        this.targetProduct = product;
        // 初始化时就保存当前的旧值,避免后续被覆盖
        this.oldName = product.getName();
        this.newName = newName;
    }

    @Override
    public void execute() {
        // 执行实际的名称修改,这里可以加入你原本的null/空字符串处理逻辑
        targetProduct.setName(newName);
        // 比如如果需要把null转为空字符串:
        // targetProduct.setName(newName == null ? "" : newName);
    }

    @Override
    public void undo() {
        // 撤销就是把名称恢复为变更前的旧值
        targetProduct.setName(oldName);
    }
}

4. 撤销管理器(处理操作历史)

import java.util.Stack;

public class UndoManager {
    // 用栈存储执行过的命令,栈顶是最近一次操作
    private Stack<Command> commandHistory = new Stack<>();

    // 执行命令并记录到历史
    public void executeCommand(Command command) {
        command.execute();
        commandHistory.push(command);
    }

    // 执行撤销操作
    public void undoLastOperation() {
        if (!commandHistory.isEmpty()) {
            Command lastCommand = commandHistory.pop();
            lastCommand.undo();
        } else {
            System.out.println("没有可撤销的操作啦!");
        }
    }
}

5. 测试使用

public class TestUndo {
    public static void main(String[] args) {
        Product myProduct = new Product();
        myProduct.setName("初始产品名称");
        UndoManager undoManager = new UndoManager();

        // 第一次修改名称
        undoManager.executeCommand(new SetNameCommand(myProduct, "新品名1"));
        System.out.println("当前名称:" + myProduct.getName()); // 输出:当前名称:新品名1

        // 第二次修改为null
        undoManager.executeCommand(new SetNameCommand(myProduct, null));
        System.out.println("当前名称:" + myProduct.getName()); // 输出:当前名称:null

        // 第一次撤销
        undoManager.undoLastOperation();
        System.out.println("撤销后名称:" + myProduct.getName()); // 输出:撤销后名称:新品名1

        // 第二次撤销
        undoManager.undoLastOperation();
        System.out.println("再次撤销后名称:" + myProduct.getName()); // 输出:再次撤销后名称:初始产品名称

        // 没有可撤销的操作了
        undoManager.undoLastOperation(); // 输出:没有可撤销的操作啦!
    }
}

可选思路:备忘录模式(Memento Pattern)

如果你的产品类有很多属性,或者需要一次性撤销所有属性的变更,可以用备忘录模式——它的核心是保存对象的快照,撤销时直接恢复到快照状态。不过这种方式会占用更多内存,适合属性不多或者需要整体回滚的场景。

简单示例(针对Product)

// 备忘录类,保存Product的状态
public class ProductMemento {
    private String name;

    public ProductMemento(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

// 修改Product类,支持生成和恢复备忘录
public class Product {
    private String name;

    // 生成当前状态的备忘录
    public ProductMemento saveToMemento() {
        return new ProductMemento(this.name);
    }

    // 从备忘录恢复状态
    public void restoreFromMemento(ProductMemento memento) {
        this.name = memento.getName();
    }

    // 原有的get/set方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

然后同样用栈存储备忘录,撤销时取出最近的快照恢复即可。

注意事项

  • 如果需要重做功能,可以再维护一个重做栈:撤销时把命令从历史栈移到重做栈,重做时再移回历史栈并执行execute。
  • 多线程环境下,栈的操作需要加锁保证线程安全。
  • 命令模式更适合单个属性的细粒度变更,备忘录模式适合对象整体状态的回滚,根据你的需求选择就行~

内容的提问来源于stack exchange,提问作者Teardrop

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 10:10:38