【问题标题】:Implementing Undo/Redo methods using Stacks使用堆栈实现撤消/重做方法
【发布时间】:2017-03-16 10:18:58
【问题描述】:

我想使用堆栈(不是摇摆)创建“撤消”和“重做”按钮。

单击“撤消”时,文本字段、列表和所有内容都需要撤消/重做。我知道我必须使用 pop、push 等。但是我在堆栈中放了什么?文本字段值?列出内容?

例子:

  • 我在学校注册了一名学生。
    • 文本字段中的学生信息已更改
    • 学生列表包含新学生

如何重置这些?

有没有办法将项目数据的当前状态存储在堆栈中?

【问题讨论】:

  • 您可能需要澄清一点。您究竟需要撤消什么?学生注册,或者人们在文本字段中写了什么?您将注册的学生存储在哪里?
  • 我需要撤消所有操作。学生对象存储在列表中。列表和文本字段需要撤消,就像 ctr+z 一样,如果之前有其他内容,则需要显示它而不是空白 txt 字段
  • 我更新了你的文字并减少了它以使其更易于阅读。

标签: java stack undo-redo


【解决方案1】:

希望我没听错,因为我不太明白您所说的“使用堆栈(不是摇摆)”是什么意思。 Stack 是一个数据结构,Swing 是一个 UI 框架。所以它们之间基本上没有任何关系。

实施撤消/重做通常使用以下方法之一来完成。您应该使用哪一个取决于您想要的撤消/重做行为。

使用命令模式撤消/重做。

实现这种方法看起来像这样:

public interface ICommand {
    void do();
    void undo();
}

public class StudentSaveCommand implements ICommand {

    public StudentSaveCommand(Student student) { ... }
    [...]
}

StudentSaveCommand 类的do() 实现中,您实现了实际保存给定学生应采取的操作,例如将数据插入数据库,保存到文件或简单地将其添加到列表中。 在undo() 实现中,您执行操作以恢复do() 实现,例如从数据库中删除、删除文件或将其从列表中删除。

要最终使用Stacks 实现撤消/重做功能,您需要执行以下操作:

public void btnSaveStudentClickHandler() {
    ICommand c = new IStudentSaveCommand(theStudentFromGUI);
    c.do();
    _redoStack.clear();
    _undoStack.push(c);
}

public void btnUndoClickHandler() {
    ICommand undoCommand = _undoStack.pop();
    undoCommand.undo();
    _redoStack.push(undoCommand); 
}

public void btnRedoClickHandler() {
    ICommand redoCommand = _redoStack.pop();
    redoCommand.do();
    _undoStack.push(redoCommand);
}

为了轻松地将模型上的更改反映到 GUI,我将实现/使用某种数据绑定。如您所见,这种方法不太适合在非常细粒度的级别上实现撤消/重做,例如在文本框中更改学生的姓名。第二种方法起作用了。

使用 Memento Pattern 撤消/重做。

使用此模式可以保存和恢复对象的状态,例如Student。使用此模式和Stacks 的撤消/重做功能的实现代码可能如下所示:

public void txtStudentNameLostFocusHandler() {
    Memento sm = student.getSavedState();
    _redoStack.clear();
    _undoStack.push(sm);
}

public void btnUndoClickHandler() {
    Memento m = _undoStack.pop();
    student.restoreSavedState(m);
    _redoStack.push(m);
}

// [...] Redo implementation straight-forward

为了反映对Student 对象所做的更改,我还希望数据绑定或实现如下方法,该方法必须在撤消/重做操作后调用:

public void updateStudentGUI(Student student) {
    txtName.setText(student.getName());
    [...]
}

要在您的撤消/重做实现中同时拥有这两种方法的优点,您还可以将它们结合起来。但是,最佳方法在很大程度上取决于规范,分别是您真正想要实现的目标。

【讨论】:

    猜你喜欢
    • 2021-06-04
    • 2011-10-15
    • 2016-03-31
    • 2023-03-10
    • 2011-03-10
    • 1970-01-01
    • 2019-08-22
    • 1970-01-01
    • 2021-03-05
    相关资源
    最近更新 更多