411 REFERENCE · Wikibooks
Computer Science Design Patterns/Memento
Reference material by Wikibooks contributors. This retains its source identity and is not 411 news reporting.
reading 25 22 May 2013 Add more illustrations. Mediator 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)