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.
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.
I would prefer to define two interfaces, but without any arguments in their methods
public interface Command {
public void execute();
}
... and for Undo Actions:
public interface UndoableCommand extends Command {
public void undo();
}
I think, this is more flexible, because you may have some interactions in your application that are not Undoable. (Like saving or loading a file)
To implement such a UndoableCommand
just write somethink like that..
public class EditorInsertCommand implements UndoableCommand {
private Editor editor;
private String textToInsert;
private String textBefore;
public EditorInsertCommand(Editor editor, String textToInsert){
this.editor = editor;
this.textToInsert = textToInsert;
}
public void execute() {
this.textBefore = editor.getText();
editor.setText(textToInsert);
};
public void undo(){
editor.setText(textBefore );
};
}
The handling of execute
and undo
, can be done via a CommandManager
as well.
精彩评论