When we looked at the Command design pattern, we noted that recording a list of every single change theoretically allows you to roll back the system to any point in time – after all, you’ve kept a record of all the modifications.
Sometimes, though, you don’t really care about playing back through all the states of the system, but you do care about being able to roll back the system to a particular state, if need be.
This is precisely what the Memento pattern does: it typically stores the state of the system and returns it as a dedicated, read-only object with no behavior of its own. This “token,” if you will, can be used only for feeding it back into the system to restore it to the state it represents.
Let’s look at an example.
Bank Account
You’ll notice that the Memento class is immutable. Imagine if you could, in fact, change the balance: you could roll back the account to a state it was never in!
This implementation is good enough, through there are some things missing. For example, you never get a memento representing the opening balance because a constructor cannot return a value. You could add an out parameter, of course, but that’s just too ugly.
Undo and Redo
What if you were to store every memento generated by BankAccount? In this case, you’d have a situation similar to our implementation of the Command pattern, where undo and redo operations are a byproduct of this recording. Let’s see how we can get undo/redo functionality with a memento.
We have now solved the problem of returning to the initial balance: the memento for the initial change is stored as well. Of course, this memento isn’t actually returned, so in order to roll back to it, well, I suppose you could implement some Reset() function or something – totally up to you.
The BankAccount class has a current member that stores the index of the latest memento. Hold on. Why do we need this? Isn’t it the case that current will always be one less than the list of changes? Only if you want to support undo/rollback operations; if you want redo operations, too, you need this!
The balance is increased by the amount you want to deposit.
A new memento is constructed with the new balance and added to the list of changes.
We increase the current value (you can think of it as a pointer into the list of changes).
The restoration process is significantly different from the one we’ve looked at earlier. First, we actually check that the memento is initialized – this is relevant because we now have a way of signaling no-ops: just return a default value. Also, when we restore a memento, we actually add that memento to the list of changes so an undo operation will work correctly on it.
We can only Undo() if current points to a change that is greater than zero. If that’s the case, we move the pointer back, grab the change at that position, apply it, and then return that change. If we cannot roll back to a previous memento, we return null, which should explain why we check for null in Restore().
Memento and Command
As I’m sure you’ve guessed, the creation of mementoes for every single change in the system is quite often unrealistic. But we’ve already seen the way undo/redo operations are defined in the Command design pattern. Just to recap, our approach to defining undo operations for Command basically meant that we would do the opposite of what the operation entailed. So, for a deposit operation, Undo() would take that money out of the account.
This is not always a realistic option either. Sometimes, the effects of an executed command are far-reaching and difficult to predict. Thus, it would make sense to use the Memento pattern to preserve the entire state of the system.
One might argue that this approach simply added an extra level of indirection: instead of saving the state of the account directly, it is saved indirectly through the use of a memento. In this example, though, instead of having individual operations return memento objects, we create a memento explicitly. This process, in turn, could also leverage the Prototype pattern in situations where the state of the object you’re trying to preserve is so complicated that writing explicit serialization code is tedious. But if that is the approach you take, you must distinguish between two types of data: the salient information about the system that must be persisted and any temporary information (e.g., private fields storing some temporary indicators) that need not be. The best advice I can give is to put all temporary information on the stack as return values – that way, you don’t hold on to it longer than necessary.
Summary
The Memento pattern is all about handing out tokens that can be used to restore the system to a prior state. Typically, the token contains all the information necessary to move the system to a particular state, and if it’s small enough, you can also use it to record all the states of the system so as to allow not just the arbitrary resetting of the system to a prior state, but controlled navigation backward (undo) and forward (redo) of all the states the system was in.
One design decision that I made in the preceding demos is to make the memento a class. This allows me to use the null value to encode the absence of a memento to operate upon. If we wanted to make it a struct instead, we would have to redesign the API so that, instead of null, the Restore() method would be able to take either a Nullable<Memento>, some Option<Memento> type (.NET doesn’t have a built-in option type yet), or a memento possessing some easily identifiable trait (e.g., a balance of int.MinValue).