Memento
Propósito
Memento guarda parte o todo el estado interno de un objeto, para que este objeto pueda ser restaurado más tarde al estado guardado por Memento. Esta operación debe ocurrir sin romper el principio del encapsulamiento.
También conocido como: Token
Motivación
Muchas veces es necesario guardar el estado interno de un objeto. Esto debido a que tiempo después, se necesita restaurar el estado del objeto, al que previamente se ha guardado.
Para facilitar el hacer y deshacer de determinadas operaciones, para lo que habrá que guardar los estados anteriores de los objetos sobre los que se opere (o bien recordar los cambios de forma incremental).
Aplicabilidad
El patrón Memento es aplicable cuando:
- Todo o parte del estado de un objeto debe ser guardado para ser restaurado más tarde.
- Cuando una interfaz directa para obtener el estado de un objeto exponga detalles de su implementación.
Estructura
Participantes
Memento
- Almacena el estado interno de un objeto Originator. El Memento puede almacenar mucho o parte del estado interno de Originator.
- Tiene dos interfaces. Una para Caretaker, que le permite manipular el Memento únicamente para pasarlo a otros objetos. La otra interfaz sirve para que Originator pueda almacenar/restaurar su estado interno, sólo Originator puede acceder a esta interfaz, al menos en teoría.
Originator
- Crea un objeto Memento conteniendo una fotografía (un instante) de su estado interno.
- Usa a Memento para restaurar su estado interno.
Caretaker
- Es responsable por mantener a salvo a Memento.
- No opera o examina el contenido de Memento.
Colaboraciones
- Un Carataker solicita un memento a un creador, lo almacena durante un tiempo y se lo devuelve a su creador, tal y como muestra el diagrama.
- A veces el Carataker no devolverá el memento a su creador, ya que el creador podría no necesitar nunca volver a un estado anterior.
- Los mementos son pasivos. Sólo el creador que creó el memento asignará o recuperará su estado.
Consecuencias
El patrón Memento tiene las siguientes consecuencias:
- Preserva la encapsulación. Originator crea un Memento y el mismo almacena su estado interno, de esta manera no es necesario exponer el estado interno como atributos de acceso público, preservando así la encapsulación.
- Simplificar Originator. Memento podría incurrir en gastos considerables si Originator tiene que almacenar y mantener a salvo una o muchas copias de su estado interno, sus responsabilidades crecerían y serían más complejas, se desviaría de su propósito disminuyendo la coherencia. Usar Mementos hace que Originator sea mucho más sencillo y coherente.
- Uso frecuente de Mementos para almacenar estados internos de gran tamaño, podría resultar costoso y perjudicar el rendimiento del sistema.
- Definiendo interfaces. Esto puede ser difícil en algunos lenguajes ya que sólo originator puede acceder al estado de memento.
- Altos costos en mantener momentos. Caretaker al no conocer detalles del estado interno de Originator, no tiene idea de cuánto espacio y tiempo se necesita para almacenar el estado interno de Originator en un Memento y restaurar su estado interno a partir de un Memento. Por lo que no puede hacer predicciones de tiempo ni de espacio.
Implementación
Hay dos cuestione que debemos tener en cuenta al implementar el patrón Memento:
- Soporte del lenguaje. Para implementar este patrón, debemos considerar que el Memento debe proporcionar una interfaz accesible sólo por Originator, en la mayoría de los lenguajes de programación, esta figura no es posible, sin embargo en C++ se puede hacer que Originator sea una clase amiga de Memento.
- Almacenando cambio incrementales. Cuando mementos son creados y pasan a través del origen (originator) con una secuencia predecible, el Memento puede guardar los cambios incrementalmente en el estado interno del origen.
Código de ejemplo
La salida en pantalla es:
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
-------------------------------------------------------------------
import java.util.*;
class Memento
{
private String
state;
public Memento(String
stateToSave)
{
state =
stateToSave;
}
public String
getSavedState()
{
return state;
}
}
class Originator
{
private String
state;
/* lots of memory
consumptive private data that is not necessary to define the
* state and
should thus not be saved. Hence the small memento object. */
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 m)
{
state = m.getSavedState();
System.out.println("Originator:
State after restoring from Memento: "+state);
}
}
class Caretaker {
private ArrayList<Memento>
savedStates = new ArrayList<Memento>();
public void
addMemento(Memento m) { savedStates.add(m); }
public Memento
getMemento(int index) { return savedStates.get(index); }
}
class MementoExample {
public static void
main(String[] args) {
Caretaker
caretaker = new Caretaker();
Originator
originator = new Originator();
originator.set("State1");
originator.set("State2");
caretaker.addMemento(
originator.saveToMemento() );
originator.set("State3");
caretaker.addMemento(
originator.saveToMemento() );
originator.set("State4");
originator.restoreFromMemento(
caretaker.getMemento(1) );
}
}
/**
* Memento pattern:
Copy the information into a another class for recovery in the future if
necessary
* @author Pperez
*
*/
public class MementoPattern {
public void
main(String args[]){
RegularClass
regularClass = new RegularClass();
regularClass.setData("First
Report");
System.out.println(regularClass.data);
regularClass.makeBackup();
regularClass.setData("Second
Report");
System.out.println(regularClass.data);
regularClass.recoverBackup();
System.out.println(regularClass.data);
}
public class
Memento{
public
String memoryData;
public
Memento(String data){
this.memoryData=data;
}
public
String recoverOldInformation(){
return
memoryData;
}
}
public class
RegularClass{
Memento
memento;
String
data;
public
RegularClass(){
}
public
void setData(String data){
this.data
= data;
}
public
void makeBackup(){
memento = new Memento(data);
}
public
void recoverBackup(){
data
= memento.recoverOldInformation();
}
}
}
El Patrón de Diseño Memento se utiliza para situaciones donde se requiera una operación de restauración “UNDO”, como en los graficadores y procesadores de texto.
Patrones relacionados
- Command: Puede usar “Mementos” para guardar el estado de operaciones restaurables.
- Iterator: “Mementos” puede ser usado con Iterator para buscar colecciones para estados específicos.
Referencias
- Design Patterns Elements of Reusable Object-Oriented Software, GoF.
- http://www.ldc.usb.ve/~mgoncalves/IS2/sd07/grupo3.pdf
- http://kybele.escet.urjc.es/documentos/SI/Patrones/15_MEMENTO.ppt
- http://www.freewebz.com/amanecer/personal/papers/paper.memento.pdf
No hay comentarios:
Publicar un comentario