25% developed

Memento

From Wikibooks, open books for an open world
Jump to navigation Jump to search

Mediator Computer Science Design Patterns
Memento
Model–view–controller

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
Model–view–controller


You have questions about this page?
Ask it here:


Create a new page on this book: