Sunday, January 24, 2010

Implementing Undo: A working approach

When I realized that the need for an extra parameter really hurts the usability, I had to rethink the way of storing moves. Additionally, moves should be executed immediately, and not simply stored and scheduled for later execution, to make the semantics feel more natural.
By the way, I changed the  name from Move to Edit, since that is the classic name for undoable stuff. Not that it would matter too much...

I didn't want to give up the tree, but needed a way to get the parent without using a parameter. Gladly, I found a solution that makes it pretty easy to create the right hierarchy. In a nutshell, the node that is currently appended to is stored in the controller, and when a node is created, it automatically finds the controller that belongs to the game.
This way, I was able to keep my tree structure and only had to do minimal changes. First, I had to change the Edit constructor to add itself to the controller. Second, the controller needed some notion of the current parent node.

Well, there were some problems on the way... I wanted to automatically execute edits at creation. the problem is the order of initialization. Imagine this:

abstract class Edit {
    public Move() {
        //add to the controller...

        //then:
        execute();
    }
    
    public abstract void execute();
    
    public abstract void rollback();
}

class MyEdit extends Edit {
    private Object importantValue;
    
    public MyEdit(Object importantValue) {
        super();
        this.importantValue = importantValue;
    }
    
    public abstract void execute() {
        //do something with importantValue
    }

    public abstract void rollback() {
        //undo something with importantValue
    }
}


execute is called in the Edit Constructor, which is called at the beginning of initialization, before importantValue is set! Although it's logical that the superclass must be initialized first - starting with Object, which reserves memory on the heap to store atributes in the first place - this leads to the requirement for some annoying workarounds. For this, it's just to move the execute out of the constructor - it is essentially done after constructing any move. I will show another workaround later - for the random number generator - until then, my new code is online; i had a hard time with subclipse, but everything's alright now. feel free to peek into laterna.magica.edit, and laterna.magica.timestamp; yet the only classes to use this new development

No comments: