Search code examples
javamodel-view-controllerdesign-patternsmemento

Implements undoable command - Java


I'm currently programming a little text editor (project for school) and I'm having some troubles finding a good and clean way to manage the undoable commands.

(It is not a code review question as it's not only about improvement. I need to change my code if I want my application to work as I want it to)

Here's how my application works: I have a concrete subject that holds the buffer that is actually an arraylist of characters. Whenever this arraylist is modified (insertion, cut, paste...), the subject updates the observers - which consist in only one gui for the moment - (MVC pattern).

What I was doing until now for the undo and redo was saving the whole state of the buffer (through a memento), which is working fine but:

  • could obviously be improved.
  • is not working for the new feature that I need to implement (recording user actions so he can play them back whenever he wants to)

I can't figure out how to save the commands instead of the buffer, and if it is the good way to go.

Here's some chunks of the program (for the insert command):

"GUI".java

...

class KeyboardListener extends KeyAdapter { 

    @Override
    public void keyPressed(KeyEvent e) {
            ...
            commandManager.executeCommandInsert(caretStart, caretStop, e.getKeyChar());
            ...
    }
    ...
}

CommandManager.java

...

public void executeCommandInsert(int start, int end, char character) {  
    addMementoUndo();   
    commandInsert.setCarets(start, end);
    commandInsert.setChar(character);
    commandInsert.execute();    
}

public void addMementoUndo() 
{
    TextConcrete text = TextConcrete.getInstance();
    this.commandsUndo.add(0, text.createMemento());
    if(recordMode){
        this.recordings.add(text.createMemento());
    }
}
...

CommandInsert.java

...
public void execute(){
   TextConcrete text = TextConcrete.getInstance();
   text.insert(this.start, this.end, this.character);
}
...

TextConcrete.java

...
public void insert(int start, int end, char character){
    //inserting in ArrayList
}

public CommandUndoable createMemento(){
   CommandUndoable mem = new CommandUndoable();
   mem.setState(getState());
   return mem;
}

public String getState(){
   StringBuilder temp = new StringBuilder();
   for(int idx = 0; idx < this.state.size(); idx++){
       temp.append(this.state.get(idx));
   }    
   return temp.toString();
}
...

The CommandUndoable is just the memento that save the state of the buffer and then is saved in a list of memento in the CareTaker (CommandManager).

Any help would really be appreciated.


Solution

  • You could create a Undoable interface that commands can implement such as:

    public interface Undoable {
    
       public void do(Editor text)
    
       public void undo(Editor text)
    
    }
    

    where Editor is the model of your editor that you can insert text into, remove text from, or manipulate with other commands. A command implementing the interface only needs to know how to 'do' and 'undo' itself, so you do not need to store a copy of the text buffer at each step, just store a List of the commands.