Как мне реализовать простую отмену / повтор для действий в Java?
Я создал редактор XML, и я застрял на последнем этапе: добавление функции отмены / повтора.
Мне нужно только добавить отмену / повтор, когда пользователи добавляют элементы, атрибуты или текст в JTree.
Я все еще новичок в этом, но сегодня в школе я попытался (безуспешно) создать два объекта стека [], которые называются отменить и повторить, и добавить в них выполняемые действия.
Например, у меня есть:
Action AddElement() {
// some code
public void actionPerformed(ActionEvent e) {
performElementAction();
}
}
executeElementAction просто добавляет элемент к JTree.
Я хочу добавить способ добавить это действие в мой стек отмены. Есть ли простой способ просто отменить.push(все выполненное действие) или что-то?
Извините за то, что я звучу как злодей, но это то, что я есть:(
5 ответов
TL;DR: Вы можете поддерживать отмены и повторные действия, реализуя шаблоны Command и Memento ( Design Patterns - Gama et. Al).
Образец Памяти
Этот простой шаблон позволяет вам сохранять состояния объекта. Просто оберните объект в новый класс, и при каждом изменении его состояния обновляйте его.
public class Memento
{
MyObject myObject;
public MyObject getState()
{
return myObject;
}
public void setState(MyObject myObject)
{
this.myObject = myObject;
}
}
Шаблон команд
Шаблон Command хранит исходный объект (который мы хотим отменить / повторить) и объект памятки, который нам нужен в случае отмены. Кроме того, определены 2 метода:
- выполнить: выполняет команду
- unExecute: удаляет команду
Код:
public abstract class Command
{
MyObject myObject;
Memento memento;
public abstract void execute();
public abstract void unExecute();
}
Определите логические "Действия", которые расширяют Команду (например, Вставка):
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
и так далее. Кроме того, один и тот же шаблон применим к любому типу объекта, что делает дизайн многоразовым и изменяемым в различных случаях использования.
Взгляните на шаблон команд, его использование включает в себя реализацию функций отмены / повтора.
Этот урок объясняет основы Командного шаблона и механизма отмены / повтора для Swing. Надеюсь, поможет.
You have to define undo(), redo() operations along with execute() in Command interface itself
,
пример:
interface Command {
void execute() ;
void undo() ;
void redo() ;
}
Определите состояние в вашем классе ConcreteCommand. В зависимости от текущего состояния после метода execute(), вы должны решить, следует ли добавить команду в стек отмены или стек повторения, и принять соответствующее решение.
Взгляните на эту статью об отмене команды для лучшего понимания.
Я бы попытался создать класс Action с классом AddElementAction, унаследованным от Action. AddElementAction может иметь методы Do () и Undo (), которые будут добавлять / удалять элементы соответственно. Затем вы можете оставить два стека действий для отмены / повтора и просто вызвать Do()/Undo() на верхнем элементе, прежде чем его вытолкнуть.