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;