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 the state 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 momento. 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 wanted 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 to 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().
Using Memento for Interop
Sometimes, managed code is not enough. For example, you need to run some calculations on the GPU and those are (typically) programmed using CUDA C and the like. You end up having to use a C or C++ library from your C# code, so you make calls from the managed (.NET) side to the unmanaged (native code) side.
This isn’t really a problem if you want to pass simple bits of data, such as numbers or arrays, back and forth. .NET has functionality for pinning an array and sending it to the “unmanaged” side for processing. It works fine, most of the time.
The problems arise when you allocate some object-oriented construct (i.e., a class) inside unmanaged code and want to return that to the managed caller. Nowadays, this is typically handled by serializing (encoding) all the data on one side and then unpacking it on the other side. There are plenty of approaches here, including simple ones such as returning XML or JSON or complicated, industry-grade solutions such as Google’s Protocol Buffers.
In some cases, though, you don’t really need to return the full object itself. Instead, you simply want to return a handle so that this handle can be subsequently used on the unmanaged side again. You don’t even need the extra memory traffic passing objects back and forth. There are many reasons why you’d want to do this, but the main reason is that you want only one side to manage the object’s lifetime, since managing it on both sides is a nightmare that nobody really needs.
What you do in this case is you return a Memento. This can be anything – a string identifier, an integer, a Globally Unique Identifier (GUID) – anything that lets you refer to the object later on. The managed side then holds on to the token and uses that token to pass back to the unmanaged side when some operations on the underlying object are required.
This approach introduces an issue with lifetime management. Suppose we want the underlying object to live for as long as we have the token. How can we implement this? Well, this would mean that, on the unmanaged side, the token lives forever, whereas on the managed side, we wrap it in an IDisposable with the Dispose() method sending a message back to the unmanaged side that the token has been disposed. But what if we copy the token and have two or more instances of it? Then we end up having to build a reference-counted system for tokens: something that is quite possible but introduces extra complexity in our system.
There is also a symmetric problem: what if the managed side has destroyed the object that the token represents? If we try to use the token, additional checks need to be made to ensure the token is actually valid, and some sort of meaningful return value needs to be given to the unmanaged call in order to tell the managed side that the token has gone stale. Again, this is extra work.
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 demos earlier 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).