Memento Design Pattern in Delphi

A Memento is an object that stores a snapshot of the internal state of another object - the memento's originator, according to GoF.

In the following example the originator class is a class that does some calculations named TCalculator. The recall-class must be a friend class of the originator class in order to access the private variables that characterize the current state. In delphi you can realize this if the two classes are defined in the same unit.

A concrete implementation of the Memento design pattern looks like this:

TCalculator = class 
private 
  ValueA,ValueB,Interval : extended; 
  Strings : TStringList; 
public 
  //... 
end;           

TCalculatorRecall = class 
private 
  RefObject : TCalculator; 
  ValueA,ValueB,Interval : extended; 
  Strings : TStringList; 
public 
  constructor Create(Calculator : TCalculator); 
  destructor Destroy; override; 
end; 

constructor TCalculatorRecall.Create(Calculator: TCalculator); 
begin 
  inherited Create; 
  RefObject := Calculator; 
  ValueA := RefObject.ValueA; 
  ValueB := RefObject.ValueB; 
  Interval := RefObject.Interval; 
  Strings := TStringList.Create; 
  Strings.Assign(RefObject.Strings); 
end; 

destructor TCalculatorRecall.Destroy; 
begin 
  RefObject.ValueA := ValueA; 
  RefObject.ValueB := ValueB; 
  RefObject.Interval := Interval; 
  RefObject.Strings.Assign(Strings); 
  Strings.Free; 
  inherited Destroy; 
end; 

The following lines demonstrate how to use this class:

  // Store state of object 
  CalculatorRecall := TCalculatorRecall.Create(Calculator); 

  // Change the state of object to do some calculations 
  Calculator.ValueA := ... 
  Calculator.DoSomething; 

  // Restore the original state 
  CalculatorRecall.Destroy; 

Examples from the VCL for the Memento Design Pattern are TFontRecall, TPenRecall and tBrushRecall, three new available classes in Delphi 6 that are derived from TRecall. Of course you can also define your own classes in this way. If you do this, consider two important points:

  • Derive the originator class from TPersistent
  • Implement the Assign procedure

Then our example looks like this:

TCalculator = class(TPersistent) 
private 
  ValueA,ValueB,Interval : extended; 
  Strings : TStringList; 
  //... 
public 
  //... 
  procedure Assign(Source: TPersistent); override; 
  //... 
end; 

TCalculatorRecall = class(TRecall) 
public 
  constructor Create(ACalculator : TCalculator); 
end; 

procedure TCalculator.Assign(Source: TPersistent); 
var RefObject : TCalculator; 
begin 
  if Source is TCalculator then 
  begin 
    RefObject := Source as tCalculator; 
    ValueA    := RefObject.ValueA; 
    ValueB    := RefObject.ValueB; 
    Interval  := RefObject.Interval; 
    //... 
    Strings.Assign(RefObject.Strings); 
    //... 
  end else 
    inherited Assign(Source); 
end; 

constructor TCalculatorRecall.Create(ACalculator: TCalculator); 
begin 
  inherited Create(TCalculator.Create, ACalculator); 
end;

Code examples