Thursday, 1 October 2009

Implementación de un patrón Memento

El patrón Memento tiene la finalidad de almacenar el estado de un objeto para su posterior recuperación de manera sencilla. Lo que hay que hacer es crear una estructura que tendrá en cuenta el estado de nuestros objetos en un instante determinado y luego poder recuperar el instante adecuado. De ésta manera podemos modificar el estado de nuestros objetos y lo que hacemos es como una fotografía (snapshot) de su estado actual. Después de hacer las modificaciones podemos "deshacer" (undo) las modificaciones volviendo a la fotografía anterior. Podemos ir guardando tantas fotos como queramos y navegar por éstas de manera fácil.

El patrón Memento define 3 roles diferentes:
  1. Originator - El objeto que sabe cómo guardarse.
  2. CareTaker - el objeto que se sabe por qué y cuando el Originator tiene que guardarse y restaurarse.
  3. Memento - la casilla de almacenado que el Originator escribe y lee y es guiada por el CareTaker.
La estructura básica es la siguiente:

Al implementar este patrón puedo salvar el diferente estado de las cajas (TBox) y luego volver a su estado anterior. Debido a que ahora podemos alinear las cajas en una determinada posición, me interesa poder volver a su estado anterior ya que ahora podemos equivocarnos y alinear los objetos de una forma no deseada, como por ejemplo:

1. Disponemos de las cajas mal posicionadas:

2. Las alineamos (Top Alignment) y guardamos su estado:

3. Cometemos un error con el alineamiento:
En éste punto, tenemos las cajas mal posicionadas, y con un simple click del mouse, podemos hacer "undo" y volver al estado anterior guardado para evitar éste tipo de errores.

  • Implementando la solución:
Aquí os dejo la estructura y el código fuente de la solución con Delphi. Se parece mucho a la solución que corre por la red en java ya que tiene la misma estructura.

El código fuente (uMemento.pas):


unit uMemento;

interface

uses
uBox, contnrs;

type
TMemento = class(TObject)
FboxListState: TBoxList;
public
constructor Create(boxListState: TBoxList);
function getSavedState(): TBoxList;
end;

TOriginator = class(TObject)
FboxListState: TBoxList;
public
procedure SetState(boxListState: TBoxList);
function SaveToMemento(): TMemento;
function restoreFromMemento(memento: TMemento): TBoxList;
end;

TCareTaker = class(TObject)
FSavedStates: TObjectList;
public
constructor Create();
destructor Destroy(); override;
procedure addMemento(memento: TMemento);
function thereAreMementos(): boolean;
function getLastMemento(): TMemento;
end;

implementation

uses
SysUtils;

{ TMemento }

constructor TMemento.Create(boxListState: TBoxList);
begin
Self.FBoxListState := boxListState.Clone;
end;

function TMemento.getSavedState: TBoxList;
begin
result := Self.FBoxListState;
end;

{ TOriginator }

function TOriginator.restoreFromMemento(memento: TMemento): TBoxList;
begin
Self.FboxListState := memento.getSavedState;
result := Self.FboxListState;
end;

function TOriginator.SaveToMemento: TMemento;
begin
result := TMemento.Create(Self.FBoxListState);
end;

procedure TOriginator.SetState(boxListState: TBoxList);
begin
Self.FBoxListState := BoxListState;
end;

{ TCareTaker }

procedure TCareTaker.addMemento(memento: TMemento);
begin
FSavedStates.Add(memento);
end;

constructor TCareTaker.Create;
begin
FSavedStates := TObjectList.Create();
end;

destructor TCareTaker.Destroy;
begin
FreeAndNil(FSavedStates);
inherited;
end;

function TCareTaker.getLastMemento(): TMemento;
begin
if (FsavedStates.Count > 0) then
result := (FSavedStates.Items[FsavedStates.Count - 1] as TMemento);
FSavedStates.Remove(FSavedStates.Items[FsavedStates.Count - 1]);
end;

function TCareTaker.thereAreMementos: boolean;
begin
result := (FsavedStates.Count > 0);
end;

end.


La llamada para guardar el estado de las cajas:


procedure TfrmMain.SaveState1Click(Sender: TObject);
begin
Originator.SetState(boxList);
CareTaker.addMemento(Originator.SaveToMemento());
end;


La recuperación del último estado (undo):


procedure TfrmMain.UndoState1Click(Sender: TObject);
begin
if CareTaker.thereAreMementos() then
boxList := Originator.restoreFromMemento(CareTaker.getLastMemento());
end;


Espero que os sirva de ayuda!.

  • Enlaces de interés:
Source Making.



0 comments:

Post a Comment