Better Memento
Today I asked myself - how to use Memento design pattern to store object state in a more safe and effective way? Of course by automating some things using some base implementation. Read about why I needed better Memento and how did I achieved nice combination of Memento and Command design patterns. Full code is given.
In game development programmers constantly meet up with standard problem. Let’s say I have some model class for Planet, objects of that model are being serialized and sent through the network to the game server, but in other hand are used in client. The problem appears when one wants to change state of this object by “animating” it’s change rather than doing this in an instant.
Straight to the example. Let’s say I have planet containing 10 fleets count. Now server sends me information about 5 incoming fleets onto this planet, so it should be 15 altogether. I have several ways to animate this for player. Basic solution is to create some temporary variable that will contain the remaining difference when animating. For current example, we have 10 fleets, then there comes 1 fleet (and we change fleets count variable to 11), we animate flying spaceship, then there comes second (and change count to 12), we animate this, etc. and there’s fifth one (we change variable to 15) which is the last one.
The problem
It’s ok, effective, etc. But the problem is when we have more such changes, e.g. 20 variables (parameters of object) that could be changed (it may be even position, rotation, etc.) when server sends new data. It’s not nice to create 20 temporary variables for animation purpose (but yeah, it’s being done quite often). The purpose is that we have to remember about those temporaries when (de)serializing object from/to the network, by adding NonSerializable attribute or so.
Another problem is that it is hard to store the change of parameter groups in the history. It’s also pretty much work to store previous differences (create history of changes).
Solution
I think I managed to make it all little nicer and cleaner. What I tried to do was to merge together the Memento and Command (partly), so i could save only changes without creating new objects as standard Memento does. Let’s first look at the usage example:
// Create new planet containing 10 fleets.
Planet planet = new Planet(10);
// Add 2 fleets to the planet. It sums to 12.
var state = planet.StoreState(
p => { p.FleetsCount += 2; },
p => { p.FleetsCount -= 2; }
);
// Remove 2 fleets from the planet. Back to 10.
state = state.RestorePreviousState();
The above is exactly what I wanted to achieved and I actually made it. Below you can see the interface
/// <summary>
/// Use by inheriting your own private nested class like this:
/// <code>private class Memento : MementoBase<MyClass> { /*your implementation*/ }</code>
/// </summary>
/// <typeparam name="T">type of object which state should be stored</typeparam>
public abstract class MementoBase<T>
{
private T _object;
private Action<T> _restoreMethod;
public MementoBase<T> PreviousStateMemento { get; private set; }
public int PreviousStatesCount { get; private set; }
private MementoBase() { }
public MementoBase(T obj, Action<T> changeStateMethod, Action<T> restoreMethod, MementoBase<T> previousStateMemento)
{
_object = obj;
_restoreMethod = restoreMethod;
PreviousStateMemento = previousStateMemento;
PreviousStatesCount = previousStateMemento == null ? 0 : previousStateMemento.PreviousStatesCount + 1;
// Change object's state
changeStateMethod.Invoke(obj);
}
/// <summary>
/// Restores object to the previous state and gets previous memento (containing previous restore method).
/// </summary>
/// <returns>previous state memento</returns>
public MementoBase<T> RestorePreviousState()
{
// Restore object's previous state.
_restoreMethod.Invoke(_object);
if (PreviousStateMemento == null)
{
_restoreMethod = o => { };
return this;
}
return PreviousStateMemento;
}
}
public interface IMementoObject<T>
{
MementoBase<T> StoreState(Action<T> changeStateMethod, Action<T> restoreMethod);
MementoBase<T> RestoreState();
}
The Model
In the end of the above snippet you can see IMementoObject from which our Planet class needs to inherit. When you do that (inherit) then also MementoBase implementation also will be needed.
class Planet : IMementoObject<Planet>
{
public int FleetsCount { get; set; }
public Planet(int fleetsCount)
{
FleetsCount = fleetsCount;
}
#region Private Memento
private class Memento : MementoBase<Planet>
{
public Memento(Planet obj,
Action<Planet> changeStateMethod,
Action<Planet> restoreMethod,
Memento previousStateMemento)
: base(obj, changeStateMethod, restoreMethod, previousStateMemento)
{}
}
[NonSerialized]
private Memento lastMemento;
#endregion
#region IMementoObject<Planet> interface implementation
public MementoBase<Planet> StoreState(Action<Planet> changeStateMethod, Action<Planet> restoreMethod)
{
return (lastMemento = new Memento(this, changeStateMethod, restoreMethod, lastMemento));
}
public MementoBase<Planet> RestoreState()
{
return (lastMemento = lastMemento.RestorePreviousState() as Memento);
}
public int PreviousStatesCount { get { return lastMemento.PreviousStatesCount; } }
#endregion
}
Pros and cons
All above implementation have either some pros and cons. The main benefit of using it is simply nice and clean usage. But the reason why I created is is not the same. The main reason is that I wanted to avoid recreating Model class objects, so some references won’t be missed.
I also gave basic **object state history **support for model objects to my project which is I like the most. While it stores the history of the object, it makes possible usage in the Command pattern. In the other hand the downside is that every Model class have to implement MementoBase<T>
but still it’s flexible so, e.g., you don’t have to use same Model object in every Memento if you don’t want to.
Afterword
The implementation could be optimized (e.g., to use some List<MementoBase<T>>
rather than only previous memento or using Flyweight pattern for Memento objects), could be thread-safe (it’s easy to do) or more clean to use (by using Reflection Mechanism or Serialization, which both I wanted to omit, so MementoBase<T>
may be unneeded).
For me it’s done and pretty useful. Hope you like it, enjoy.