Как реализовать простой откат/повтор для действий в java?

Я создал редактор XML, и я застрял на последнем этапе: добавив функции отмены/повтора.

Мне нужно только добавить undo/redo, когда пользователи добавляют элементы, атрибуты или текст в JTree.

Я все еще новичок в этом, но в школе сегодня я попытался (безуспешно) создать два объекта стека [], которые называются undo и redo, и добавить действия, выполненные в них.

Случай, я:

Action AddElement() {

// some code
public void actionPerformed(ActionEvent e) {

                    performElementAction();
                }
}

performElementAction фактически добавляет элемент JTree.

Я хочу добавить способ добавления этого действия в мой стек отменить. есть простой способ просто отменить .push(все действие) или что-то еще?

Извините за звучание как baddie, но это то, что я есть: (

Ответ 1

Взгляните на Command Pattern, его использование включает в себя реализацию функций отмены/повтора.

Ответ 2

TL; DR: вы можете поддерживать действия отмены и повтора, реализуя шаблоны Command и Memento (Design Patterns - Gama et al.),

Шаблон Memento

Этот простой шаблон позволяет сохранять состояния объекта. Просто оберните объект в новый класс и всякий раз, когда его состояние изменится, обновите его.

public class Memento
{
    MyObject myObject;

    public MyObject getState()
    {
        return myObject;
    }

    public void setState(MyObject myObject)
    {
        this.myObject = myObject;
    }
}

Шаблон команды

В шаблоне Command хранится исходный объект (который мы хотим поддерживать отменить/повторить) и объект памяти, который нам нужен в случае отмены. Кроме того, определены два метода:

  • выполнить: выполняет команду
  • unExecute: удаляет команду

код:

public abstract class Command
{
    MyObject myObject;
    Memento memento;

    public abstract void execute();

    public abstract void unExecute();
}

Определите логические "Действия", которые расширяют команду (например, Insert):

public class InsertCharacterCommand extends Command
{
    //members..

    public InsertCharacterCommand()
    {
        //instantiate 
    }

    @Override public void execute()
    {
        //create Memento before executing
        //set new state
    }

    @Override public void unExecute()
    {
        this.myObject = memento.getState()l
    }
}

Применение шаблонов:

Этот последний шаг определяет поведение отмены/повтора. Основная идея состоит в том, чтобы хранить стек команд, который работает как список истории команд. Для поддержки повтора вы можете сохранить вторичный указатель всякий раз, когда применяется команда отмены. Обратите внимание, что всякий раз, когда вставлен новый объект, все команды после его текущей позиции удаляются; что достигается с помощью метода deleteElementsAfterPointer, определенного ниже:

private int undoRedoPointer = -1;
private Stack<Command> commandStack = new Stack<>();

private void insertCommand()
{
    deleteElementsAfterPointer(undoRedoPointer);
    Command command =
            new InsertCharacterCommand();
    command.execute();
    commandStack.push(command);
    undoRedoPointer++;
}

private void deleteElementsAfterPointer(int undoRedoPointer)
{
    if(commandStack.size()<1)return;
    for(int i = commandStack.size()-1; i > undoRedoPointer; i--)
    {
        commandStack.remove(i);
    }
}

 private void undo()
{
    Command command = commandStack.get(undoRedoPointer);
    command.unExecute();
    undoRedoPointer--;
}

private void redo()
{
    if(undoRedoPointer == commandStack.size() - 1)
        return;
    undoRedoPointer++;
    Command command = commandStack.get(undoRedoPointer);
    command.execute();
}

Вывод:

Что делает этот проект мощным, так это то, что вы можете добавить столько команд, сколько хотите (путем расширения класса Command), например, RemoveCommand, UpdateCommand и т.д. Более того, тот же шаблон применим к любому типу объекта, что делает дизайн многократно используемым и модифицируемым в разных вариантах использования.

Ответ 3

В этом руководстве объясняется фундаментальное значение Command Pattern и механизм отмены/повтора для Swing. Надеюсь, что это поможет.

Ответ 4

Я бы попытался создать класс Action с классом AddElementAction, наследующим действие Action. AddElementAction может иметь метод Do() и Undo(), который будет соответствующим образом добавлять/удалять элементы. Затем вы можете сохранить два стека Actions для отмены/повтора и просто вызвать Do()/Undo() в верхнем элементе перед тем, как выложить его.

Ответ 5

You have to define undo(), redo() operations along with execute() in Command interface itself.

Пример:

interface Command {

    void execute() ;

    void undo() ;

    void redo() ;
}

Определите состояние в классе ConcreteCommand. В зависимости от текущего состояния после метода execute() вам необходимо решить, следует ли добавлять команду в стек Undo Stack или Redo Stack и принимать соответствующее решение.

Посмотрите на эту статью undo-redo для лучшего понимания.