Design Patterns: Memento

The Memento Design Pattern is about recording state. In a racing game there are sometimes the “ghosts” that is a shadow of your best run. These ghosts could have all their movements recorded by a memento and replayed during the game.

A memento has a simple implementation, it has a class to store the properties that will be recorded and a recorder that will store the values.

  1. public class Car    
  2. {    
  3.     public int X { getset; }    
  4.      
  5.     public int Y { getset; }    
  6.      
  7.     // the class we want to record    
  8.     // will have a Memento property    
  9.     // returning a new memento every time    
  10.     // and setting the class properties    
  11.     // from the memento    
  12.     public CarMemento Memento    
  13.     {    
  14.         get { return new CarMemento(X, Y); }    
  15.         set    
  16.         {    
  17.             X = value.X;    
  18.             Y = value.Y;    
  19.         }    
  20.     }    
  21. }    
  22.      
  23. // class with the properties that will be recorded    
  24. public class CarMemento    
  25. {    
  26.     public CarMemento(int x, int y)    
  27.     {    
  28.         X = x;    
  29.         Y = y;    
  30.     }    
  31.      
  32.     public int X { getset; }    
  33.     public int Y { getset; }    

And we need a recorder to store a list of mementos.

  1. public class CarRecorder    
  2. {    
  3.     private int currentIndex = -1;    
  4.      
  5.     private IList<CarMemento> mementos = new List<CarMemento>();    
  6.      
  7.     private Car car;    
  8.      
  9.     public CarRecorder(Car car)    
  10.     {    
  11.         this.car = car;    
  12.     }    
  13.      
  14.     public int NextIndex    
  15.     {    
  16.         get    
  17.         {    
  18.             if (mementos.Count == 0)    
  19.                 return -1;    
  20.      
  21.             int index = this.currentIndex + 1;    
  22.      
  23.             return index >= mementos.Count    
  24.                 ? mementos.Count - 1    
  25.                 : index;    
  26.         }    
  27.     }    
  28.      
  29.     public int PreviousIndex    
  30.     {    
  31.         get    
  32.         {    
  33.             if (mementos.Count == 0)    
  34.                 return -1;    
  35.      
  36.             return this.currentIndex - 1;    
  37.         }    
  38.     }    
  39.      
  40.     public void Record()    
  41.     {    
  42.         mementos.Add(car.Memento);    
  43.         this.currentIndex++;    
  44.     }    
  45.      
  46.     public void Forward()    
  47.     {    
  48.         if (NextIndex > -1)    
  49.         {    
  50.             car.Memento = mementos[NextIndex];    
  51.             this.currentIndex++;    
  52.         }    
  53.     }    
  54.      
  55.     public void Rewind()    
  56.     {    
  57.         if (PreviousIndex > -1)    
  58.         {    
  59.             car.Memento = mementos[PreviousIndex];    
  60.             this.currentIndex--;    
  61.         }    
  62.     }    

Using it:

  1. var car = new Car();    
  2. var recorder = new CarRecorder(car);    
  3.      
  4. car.X = 10;    
  5. car.Y = 10;    
  6.      
  7. recorder.Record();    
  8.      
  9. car.X = 20;    
  10. car.Y = 20;    
  11.      
  12. recorder.Record();    
  13.      
  14. car.X = 30;    
  15. car.Y = 30;    
  16.      
  17. recorder.Record();  

At this point we have 3 states saved on the recorder. We can iterate using the states by calling Forward and Rewind. Currently our car is at the following position:

  1. 30,30  

But if we call the recorder method Rewind:

  1. recorder.Rewind();  

Our car instance will now have X and Y set to 20.

  1. car.X // 20    
  2. car.Y // 20   
    We can call forward and have the values set back to 30, 30.
      1. recorder.Forward();    
      2. car.X // 30    
      3. car.Y // 30   
        In a racing game the position of the car could be recorded every time and afterwards it can be replayed from the list of mementos allowing you to race against your own ghost.