views:

87

answers:

3

How does undo work? Does it copy all the managed objects every time any of the values change? Or does it only copy the actual changes together with an information which objects were affected? Is that heavy or lightweight?

+3  A: 

The 'undo' mechanism for pretty much any language that supports Object-Oriented constructs uses the Memento Design Pattern to make it happen.

George Stocker
That's not necessarily true. Memento can burn a lot of memory. Apps I've worked on typically use a Command pattern where each Command encapsulates the logic to perform a change and to undo it. That way you're only spending memory on the difference between the object's previous and next states.
munificent
+1  A: 

See this SO question and my answer about using SQLite to implement undo.

Doug Currie
A: 

Here's a rough implementation to get you thinking. This handles your stack of undoable operations. (It doesn't handle redo, but that's easy to support by replacing the stack with a list and keeping track of the current item.)

public class Undoable {
    public static void Do(Action do, Action undo) {
        do();
        sUndoStack.Push(new Undoable(do, undo));
    }

    public static void Undo() {
        sUndoStack.Pop().mUndoCallback();
    }

    private Undoable(Action doCallback, undoCallback) {
        mDoCallback = doCallback;
        mUndoCallback = undoCallback;
    }

    private Action mDoCallback, mUndoCallback;

    // note: using a global stack here is lame, but works for demo purposes
    private static readonly Stack<Undoable> sUndoStack = new Stack<Undoable>();
}

To use this, let's say the user can change a value in some object like this:

public class Foo {
    public string Bar {
        get { return mBar; }
        set {
            if (mBar != value) {
                mBar = value;
            }
        }
    }

    private string mBar;
}

To make that operation undoable, we just change the setter to:

set {
    if (mBar != value) {
        string oldValue = mBar;
        Undoable.Do(() => mBar = value,
                    () => mBar = oldValue);
    }
}

Now, if you call Undoable.Undo() from anywhere in the application, your instance of Foo will restore the previous value of Bar. If Foo also raises an event when Bar changes (not shown here), the UI will also properly refresh on undo too.

munificent