Memento
Table of content
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)