Software Architecture
Undo/Redo using Mementos
Normal 0 The most basic element is a Memento, a Memento contains two virtual methods Commit and Revert and all the basic actions that modify your data should be derived from this class. Above this is the Context, this is a list of mementos which when performed can undo or redo an operation. Whenever a user performs an operation a new context is created and mementos added, when the operation is complete the context is closed and added to the undo stack.
To undo an operation the last context is popped of the undo stack and Reverted, this in turn reverts each of the mementos in the context. Once complete the context is moved to the redo stack. To redo an operation the last context is popped of the redo stack and Committed, this in turn commits each of the mementos in the context. Once complete the context is moved back to the undo stack. When a new context is added to the undo stack you should also clear the redo stack as the redo operations can only be applied from the previous state of the data.
Choosing what to implement, as a memento can be confusing, they should be the most basic operations that can be applied to your data. For example create, destroy, modify, select should cover most things.
Extending Undo/Redo with Transactions
A Transaction is used to manage the context and ensure that the actions are rolled back if an error or exception occurs, for each user action the code looks something like this;
Transaction transaction = new Transaction(“Do Something”);
try
{
CreateMemento create = new CreateMemento(“New Object”);
Create->Commit();
// Modify makes a copy of the data stored within the object
//
ModifyMemento modify = new ModifyMemento(create->GetObject());
// Modify The Object
// Commit the changes, if Commit fails it must revert any partial changes itself
//
modify->Commit();
transaction->Commit();
}
catch(Exception e)
{
// Revert any mementos that were committed
transaction->Cancel();
}
Note it’s advantageous to keep the code for the action outside of the code for the UI, allow the UI to call actions to perform the operations for it. This is because you can then reuse the code for the actions in another context for example in unit testing or batch processing.
Links
Design Patterns
http://www.vincehuston.org/dp/