Problema
Guardar el estado de un objeto para poder deshacerlo más tarde, sin romper la encapsulación.
Propósito
Permite implementar undo/redo guardando “fotos” del estado de un objeto en momentos específicos. El objeto puede restaurarse a cualquiera de esos estados guardados.
Concepto clave
Snapshot del estado: Como los puntos de guardado en videojuegos - capturas el estado completo en un momento y puedes volver a él cuando quieras.
Casos de uso comunes
- Sistemas de undo/redo en editores
- Checkpoints en juegos
- Transacciones con rollback
- Snapshots de configuración
- Historial de cambios
- Sistemas de backup automático
¿Quién es quién en Memento?
Actor | Lo que realmente es | Ejemplo | Analogía |
---|---|---|---|
Originator | Objeto que cambia y crea snapshots | TextEditor - crea y restaura su estado |
Videojuego (cambia constantemente) |
Memento | “Foto” del estado en un momento específico | EditorMemento - guarda contenido, cursor, selección |
Archivo de guardado (snapshot del juego) |
Caretaker | Administrador del historial de snapshots | EditorHistory - gestiona múltiples mementos |
Sistema de guardado (gestiona saves) |
Diagrama
classDiagram
namespace MementoPattern {
class Originator {
-state: State
+createMemento() Memento
+restoreFromMemento(memento)
+setState(state)
+getState() State
}
class Memento {
-state: State
+getState() State
}
class Caretaker {
-mementos: List~Memento~
+addMemento(memento)
+getMemento(index) Memento
+undo() Memento
}
}
Originator --> Memento : creates
Caretaker --> Memento : stores
Caretaker --> Originator : uses
Ejemplo práctico
classDiagram
namespace TextEditorExample {
class TextEditor {
-content: String
-cursorPosition: int
-selectionStart: int
-selectionEnd: int
+createSnapshot() EditorMemento
+restoreFromSnapshot(memento)
+type(text)
+delete()
+moveCursor(position)
}
class EditorMemento {
-content: String
-cursorPosition: int
-selectionStart: int
-selectionEnd: int
-timestamp: LocalDateTime
+getContent() String
+getCursorPosition() int
+getSelectionStart() int
+getSelectionEnd() int
}
class EditorHistory {
-snapshots: Stack~EditorMemento~
-maxHistorySize: int
+saveSnapshot(memento)
+undo() EditorMemento
+canUndo() boolean
+clear()
+getHistorySize() int
}
class EditorController {
-editor: TextEditor
-history: EditorHistory
+executeCommand(command)
+undo()
+saveCheckpoint()
}
}
TextEditor --> EditorMemento : creates
EditorHistory --> EditorMemento : stores
EditorController --> TextEditor
EditorController --> EditorHistory
Flujo de operaciones
sequenceDiagram
participant User
participant Controller
participant Editor
participant History
participant Memento
User->>Controller: type("Hello")
Controller->>Editor: createSnapshot()
Editor->>Memento: new EditorMemento(state)
Memento-->>Editor: memento
Editor-->>Controller: memento
Controller->>History: saveSnapshot(memento)
Controller->>Editor: type("Hello")
User->>Controller: type(" World")
Controller->>Editor: createSnapshot()
Editor->>Memento: new EditorMemento(state)
Controller->>History: saveSnapshot(memento)
Controller->>Editor: type(" World")
User->>Controller: undo()
Controller->>History: undo()
History-->>Controller: previous memento
Controller->>Editor: restoreFromSnapshot(memento)
Note over User: Editor state restored to "Hello"
Ventajas
- Encapsulación: No viola la encapsulación del originator
- Simplicidad: Simplifica el originator al delegar el guardado de estado
- Flexibilidad: Permite múltiples puntos de restauración
- Aislamiento: El caretaker no necesita conocer el estado interno
Desventajas
- Memoria: Puede consumir mucha memoria con muchos snapshots
- Performance: Crear snapshots puede ser costoso
- Complejidad: Manejar referencias y objetos complejos puede ser difícil
- Consistencia: Mantener consistencia entre snapshots puede ser complejo
Cuándo usar
- Necesitas implementar undo/redo
- Quieres crear checkpoints o snapshots
- Necesitas rollback de transacciones
- Quieres mantener historial de cambios sin violar encapsulación
Cuándo NO usar
- El estado del objeto es simple y fácil de recrear
- La memoria es muy limitada
- Los snapshots son muy costosos de crear
- No necesitas funcionalidad de undo