Last modified on 19 January 2014, at 07:40

Memento

Mediator Computer Science Design Patterns
Memento
Observer

Briefly, the Originator (the object to be saved) creates a snap-shot of itself as a Memento object, and passes that reference to the Caretaker object. The Caretaker object keeps the Memento until such a time as the Originator may want to revert to a previous state as recorded in the Memento object.

Implementation in Java

The following Java program illustrates the "undo" usage of the Memento Pattern.

class Originator {
    private String state;
    // The class could also contain additional data that is not part of the
    // state saved in the memento.
 
    public void set(String state) {
        System.out.println("Originator: Setting state to " + state);
        this.state = state;
    }
 
    public Memento saveToMemento() {
        System.out.println("Originator: Saving to Memento.");
        return new Memento(state);
    }
 
    public void restoreFromMemento(Memento memento) {
        state = memento.getSavedState();
        System.out.println("Originator: State after restoring from Memento: " + state);
    }
 
    public static class Memento {
        private final String state;
 
        private Memento(String stateToSave) {
            state = stateToSave;
        }
 
        private String getSavedState() {
            return state;
        }
    }
}
import java.util.List;
import java.util.ArrayList;
 
class Caretaker {
    public static void main(String[] args) {
        List<Originator.Memento> savedStates = new ArrayList<Originator.Memento>();
 
        Originator originator = new Originator();
        originator.set("State1");
        originator.set("State2");
        savedStates.add(originator.saveToMemento());
        originator.set("State3");
        // We can request multiple mementos, and choose which one to roll back to.
        savedStates.add(originator.saveToMemento());
        originator.set("State4");
 
        originator.restoreFromMemento(savedStates.get(1));  
    }
}

The output is:

Originator: Setting state to State1
Originator: Setting state to State2
Originator: Saving to Memento.
Originator: Setting state to State3
Originator: Saving to Memento.
Originator: Setting state to State4
Originator: State after restoring from Memento: State3
Implementation in C#

C# Example

using System;
 
namespace DoFactory.GangOfFour.Memento.Structural
{
  // MainApp startup class for Structural
  // Memento Design Pattern.
  class MainApp
  {
     // Entry point into console application.
    static void Main()
    {
      Originator o = new Originator();
      o.State = "On";
 
      // Store internal state
      Caretaker c = new Caretaker();
      c.Memento = o.CreateMemento();
 
      // Continue changing originator
      o.State = "Off";
 
      // Restore saved state
      o.SetMemento(c.Memento);
 
      // Wait for user
      Console.ReadKey();
    }
  }
 
  // The 'Originator' class
  class Originator
  {
    private string _state;
 
    // Property
    public string State
    {
      get { return _state; }
      set
      {
        _state = value;
        Console.WriteLine("State = " + _state);
      }
    }
 
    // Creates memento
    public Memento CreateMemento()
    {
      return (new Memento(_state));
    }
 
    // Restores original state
    public void SetMemento(Memento memento)
    {
      Console.WriteLine("Restoring state...");
      State = memento.State;
    }
  }
 
  // The 'Memento' class
  class Memento
  {
    private readonly string _state;
 
    // Constructor
    public Memento(string state) {
      this._state = state;
    }
 
    // Gets or sets state
    public string State
    {
      get { return _state; }
    }
  }
 
  // The 'Caretaker' class
  class Caretaker
  {
    private Memento _memento;
 
    // Gets or sets memento
    public Memento Memento
    {
      set { _memento = value; }
      get { return _memento; }
    }
  }
}

Another way to implement memento in C#

public interface IOriginator
{
   IMemento GetState();
}
 
public interface IShape : IOriginator
{
   void Draw();
   void Scale(double scale);
   void Move(double dx, double dy);
}
 
public interface IMemento
{
   void RestoreState();
}
 
public class CircleOriginator : IShape
{
   private class CircleMemento : IMemento
   {
      private readonly double x;
      private readonly double y;
      private readonly double r;
      private readonly CircleOriginator originator;
 
      public CircleMemento(CircleOriginator originator)
      {
         this.originator = originator;
         x = originator.x;
         y = originator.y;
         r = originator.r;
      }
 
      public void RestoreState()
      {
         originator.x = x;
         originator.y = y;
         originator.r = r;
      }
   }
 
   double x;
   double y;
   double r;
 
   public CircleOriginator(double x, double y, double r)
   {
      this.x = x;
      this.y = y;
      this.r = r;
   }
 
   public void Draw()
   {
      Console.WriteLine("Circle with radius {0} at ({1}, {2})", r, x, y);
   }
 
   public void Scale(double scale)
   {
      r *= scale;
   }
 
   public void Move(double dx, double dy)
   {
      x += dx;
      y += dy;
   }
 
   public IMemento GetState()
   {
      return new CircleMemento(this);
   }
}
 
public class RectOriginator : IShape
{
   private class RectMemento : IMemento
   {
      private readonly double x;
      private readonly double y;
      private readonly double w;
      private readonly double h;
      private readonly RectOriginator originator;
 
      public RectMemento(RectOriginator originator)
      {
         this.originator = originator;
         x = originator.x;
         y = originator.y;
         w = originator.w;
         h = originator.h;
      }
 
      public void RestoreState()
      {
         originator.x = x;
         originator.y = y;
         originator.w = w;
         originator.h = h;
      }
   }
 
   double x;
   double y;
   double w;
   double h;
 
   public RectOriginator(double x, double y, double w, double h)
   {
      this.x = x;
      this.y = y;
      this.w = w;
      this.h = h;
   }
 
   public void Draw()
   {
      Console.WriteLine("Rectangle {0}x{1} at ({2}, {3})", w, h, x, y);
   }
 
   public void Scale(double scale)
   {
      w *= scale;
      h *= scale;
   }
 
   public void Move(double dx, double dy)
   {
      x += dx;
      y += dy;
   }
 
   public IMemento GetState()
   {
      return new RectMemento(this);
   }
}
 
public class Caretaker
{
   public void Draw(IEnumerable<IShape> shapes)
   {
      foreach(IShape shape in shapes)
      {
         shape.Draw();
      }
   }
 
   public void MoveAndScale(IEnumerable<IShape> shapes)
   {
      foreach(IShape shape in shapes)
      {
         shape.Scale(10);
         shape.Move(3, 2);
      }
   }
 
   public IEnumerable<IMemento> SaveStates(IEnumerable<IShape> shapes)
   {
      List<IMemento> states = new List<IMemento>();
      foreach(IShape shape in shapes)
      {
         states.Add(shape.GetState());
      }
      return states;
   }
 
   public void RestoreStates(IEnumerable<IMemento> states)
   {
      foreach(IMemento state in states)
      {
         state.RestoreState();
      }
   }
 
   public static void Main()
   {
      IShape[] shapes = { new RectOriginator(10, 20, 3, 5), new CircleOriginator(5, 2, 10) };
 
      //Outputs:
      // Rectangle 3x5 at (10, 20)
      // Circle with radius 10 at (5, 2)
      Draw(shapes);
 
      //Save states of figures
      IEnumerable<IMemento> states = SaveStates(shapes);
 
      //Change placement of figures
      MoveAndScale(shapes);
 
      //Outputs:
      // Rectangle 30x50 at (13, 22)
      // Circle with radius 100 at (8, 4)
      Draw(shapes);
 
      //Restore old placement of figures
      RestoreStates(states);
 
      //Outputs:
      // Rectangle 3x5 at (10, 20)
      // Circle with radius 10 at (5, 2)
      Draw(shapes);
   }
}
Implementation in Pascal
{$apptype console}
program Memento;
 
uses
  SysUtils, Classes;
 
type
  TMemento = class
  private
    _state: string;
    function GetSavedState: string;
  public
    constructor Create(stateToSave: string);
    property SavedState: string read GetSavedState;
  end;
 
  TOriginator = class
  private
    _state: string;
    // The class could also contain additional data that is not part of the
    // state saved in the memento.
    procedure SetState(const state: string);
  public
    function SaveToMemento: TMemento;
    procedure RestoreFromMemento(Memento: TMemento);
    property state: string read _state write SetState;
  end;
 
  TCaretaker = class
  private
    _memento: TMemento;
  public
    property Memento: TMemento read _memento write _memento;
  end;
 
  { TMemento }
 
constructor TMemento.Create(stateToSave: string);
begin
  _state := stateToSave;
end;
 
function TMemento.GetSavedState: string;
begin
  writeln('restoring state from Memento');
  result := _state;
end;
 
{ TOriginator }
 
procedure TOriginator.RestoreFromMemento(Memento: TMemento);
begin
  _state := Memento.SavedState;
  writeln('Originator: State after restoring from Memento: ' + state);
end;
 
function TOriginator.SaveToMemento: TMemento;
begin
  writeln('Originator: Saving to Memento.');
  result := TMemento.Create(state);
end;
 
procedure TOriginator.SetState(const state: string);
begin
  writeln('Originator: Setting state to ' + state);
  _state := state;
end;
 
var
  originator: TOriginator;
  c1,c2: TCaretaker;
begin
  originator := TOriginator.Create;
  originator.SetState('State1');
  originator.SetState('State2');
  // Store internal state
  c1 := TCaretaker.Create();
  c1.Memento := originator.SaveToMemento();
  originator.SetState('State3');
  // We can request multiple mementos, and choose which one to roll back to.
  c2 := TCaretaker.Create();
  c2.Memento := originator.SaveToMemento();
  originator.SetState('State4');
 
  // Restore saved state
  originator.RestoreFromMemento(c1.Memento);
  originator.RestoreFromMemento(c2.Memento);
  c1.Memento.Free;
  c1.Free;
  c2.Memento.Free;
  c2.Free;
  originator.Free;
end.
Implementation in Scala
import java.util.Date
import common.patterns.Originator.Memento
import scala.collection.immutable.TreeMap
 
object Originator {
  case class Memento(x: Int, y: Int, time: Date)
}
 
case class Originator(var x: Int, var y: Int) {
  def setState(x: Int, y: Int) {
    this.x = x
    this.y = y
  }
 
  def createMemento(): Memento = Memento(x, y, new Date())
 
  def restoreFromMemento(restore: Memento) {
    this.x = restore.x
    this.y = restore.y
  }
}
 
object Caretaker extends App {
  var states: TreeMap[Date, Memento] = new TreeMap
  val originator = new Originator(0, 0)
  var memento = originator.createMemento()
  states += (memento.time -> memento)
 
  Thread.sleep(100)
  originator.setState(5, 4)
  memento = originator.createMemento()
  states += (memento.time -> memento)
 
  Thread.sleep(100)
  originator.setState(10, 10)
  memento = originator.createMemento()
  states += (memento.time -> memento)
 
  originator.restoreFromMemento(states(states.drop(1).firstKey))  //restore second state
  println(originator)
}

Output:

   Originator(5,4)


Clipboard

To do:
Add more illustrations.


Mediator Computer Science Design Patterns
Memento
Observer


You have questions about this page?
Ask it here:


Create a new page on this book: