💾 El Patrón Memento: Viajando en el tiempo
El Patrón Memento (Recuerdo), tiene un objetivo que todos amamos: permitir volver atrás. Su misión es guardar el estado de un objeto para poder restaurarlo después, pero sin romper las reglas de privacidad del objeto.
El problema que resuelve es este:
¿Cómo guardas una copia exacta de un objeto (incluyendo sus variables privadas) para poder restaurarlo luego, sin exponer esas variables privadas al mundo exterior y romper el encapsulamiento?
La respuesta es dejar que el propio objeto cree una “instantánea” (Memento) de sí mismo. Solo él puede leerla, pero otros pueden guardarla.

🎮 1. Un ejemplo sencillo y muy visto: Guardar Partida
Imagina que estás jugando a un videojuego difícil (El Originador):
- Antes de enfrentarte al jefe final, le das a “Guardar Partida”.
- El juego crea un archivo de guardado (El Memento) con tu salud, inventario y posición.
- Tú (El Cuidador) guardas ese archivo en la Memory Card. No sabes qué hay dentro (bits y bytes raros), solo sabes que es tu partida.
- Si el jefe te mata, cargas el archivo y vuelves exactamente a como estabas.
Concepto (Analogía) | Rol en Memento | Tarea Principal |
|---|---|---|
El Juego / Personaje | Originador (Originator) | El objeto cuyo estado queremos guardar y restaurar. |
Archivo de Guardado | Memento | Objeto de valor inmutable que contiene el estado. |
La Consola / Jugador | Cuidador (Caretaker) | Guarda los mementos pero no los modifica ni inspecciona. |
🛠️ 2. Los tres pilares del patrón Memento
1. Originador
- Crea un memento con su estado actual (
guardar()). - Restaura su estado a partir de un memento (
restaurar(m)).
2. Memento
- Objeto simple que almacena el estado.
- Debe ser opaco para el Cuidador (no dejarle ver los datos) pero accesible para el Originador.
3. Cuidador (Caretaker)
- Es responsable de guardar los mementos (historial).
- Nunca opera sobre el contenido del memento.
🤔 1. ¿Por qué el Cuidador no debe modificar el Memento?
↩️ 3. Otro ejemplo muy clásico: Deshacer (undo)
El uso más conocido es el comando CTRL+Z en editores de texto. Cada vez que escribes, el editor guarda un Memento en una pila. Al deshacer, saca el último Memento y restaura el estado.
🤔 2. ¿Qué desventaja obvia tiene guardar muchos Mementos?
✅ 4. ¿Por qué usarlo?
- Encapsulamiento: Puedes guardar el estado privado sin hacerlo público.
- Simplificación: El Originador no tiene que gestionar el historial de versiones, eso lo hace el Cuidador.
❌ 5. Desventaja a considerar
- Consumo de Memoria: Si el estado del objeto es grande y guardas muchas copias (historial largo), te puedes comer la RAM del servidor.
🤔 3. ¿Es el patrón Memento la única forma de implementar 'Deshacer'?
💡 6. Conclusión
El Memento es tu salvación si necesitas implementar “Deshacer” o transacciones con rollback. Es la forma limpia de viajar en el tiempo dentro de tu aplicación.

🧠 7. Ejemplo práctico en PHP
Vamos a hacer un editor de texto simple con historial.
<?php
// 🔹 1. Memento
class EditorMemento
{
private string $contenido;
public function __construct(string $contenido)
{
$this->contenido = $contenido;
}
public function getContenido(): string
{
return $this->contenido;
}
}
// 🔹 2. Originador
class EditorTexto
{
private string $contenido = '';
public function escribir(string $texto): void
{
$this->contenido .= $texto;
echo "📝 Escrito: '$texto'. Contenido actual: '{$this->contenido}'\n";
}
public function guardar(): EditorMemento
{
echo "💾 Guardando estado...\n";
return new EditorMemento($this->contenido);
}
public function restaurar(EditorMemento $memento): void
{
$this->contenido = $memento->getContenido();
echo "↩️ Restaurado a: '{$this->contenido}'\n";
}
}
// 🔹 3. Cuidador
class Historial
{
private array $mementos = [];
private EditorTexto $editor;
public function __construct(EditorTexto $editor)
{
$this->editor = $editor;
}
public function backup(): void
{
$this->mementos[] = $this->editor->guardar();
}
public function deshacer(): void
{
if (empty($this->mementos)) {
echo "⚠️ No hay nada que deshacer.\n";
return;
}
$memento = array_pop($this->mementos);
$this->editor->restaurar($memento);
}
}
// 🔹 4. Uso
$editor = new EditorTexto();
$historial = new Historial($editor);
$editor->escribir("Hola");
$historial->backup(); // Guardamos "Hola"
$editor->escribir(" Mundo");
$historial->backup(); // Guardamos "Hola Mundo"
$editor->escribir(" Cruel");
// Contenido: "Hola Mundo Cruel"
echo "\n--- Ups, me arrepiento ---\n";
$historial->deshacer(); // Vuelve a "Hola Mundo"
$historial->deshacer(); // Vuelve a "Hola"
// 🖥️ Salida:
// 📝 Escrito: 'Hola'. Contenido actual: 'Hola'
// 💾 Guardando estado...
// 📝 Escrito: ' Mundo'. Contenido actual: 'Hola Mundo'
// 💾 Guardando estado...
// 📝 Escrito: ' Cruel'. Contenido actual: 'Hola Mundo Cruel'
//
// --- Ups, me arrepiento ---
// ↩️ Restaurado a: 'Hola Mundo'
// ↩️ Restaurado a: 'Hola'
EA, ¡saluditos y nos vemos en los bares! 🍻