Python Patrón Memento (Memento)
El patrón Memento es un patrón de diseño de comportamiento que permite capturar y guardar el estado interno de un objeto sin romper la encapsulación, para poder restaurarlo a un estado anterior cuando sea necesario.
Imagina que estás jugando a un juego que ofrece la función de "guardar partida". Puedes guardar el progreso actual en cualquier momento; si luego pierdes o quieres reintentar un nivel, puedes cargar la partida guardada anteriormente. El patrón Memento es la solución de programación para implementar esta función de "guardar-cargar".
¿Por qué necesitamos el patrón Memento?
Escenarios de aplicación
- Editor de texto: implementar las funciones de deshacer (Undo) y rehacer (Redo)
- Desarrollo de juegos: guardar el progreso y el estado del juego
- Software gráfico: soportar el historial de operaciones
- Procesamiento de transacciones: revertir a un estado anterior cuando una operación falla
Problemas que resuelve
- Necesita guardar el estado de un objeto sin exponer los detalles internos del objeto
- Proporciona un mecanismo de restauración de estado, compatible con la función de "arrepentimiento"
- Mantiene la encapsulación del objeto, evitando mezclar la lógica de guardado de estado con la lógica de negocio
Componentes principales del patrón Memento
El patrón Memento incluye tres roles principales:
1. Originador (Originator)
- El objeto que necesita guardar el estado
- Crea un memento para registrar el estado actual
- Usa el memento para restaurar el estado anterior
2. Memento (Memento)
- Almacena el estado interno del objeto originador
- Impide que objetos distintos del originador accedan al contenido del memento
3. Administrador (Caretaker)
- Se encarga de guardar los mementos
- No puede operar ni inspeccionar el contenido del memento

Implementación del patrón Memento en Python
Implementemos el patrón Memento mediante un ejemplo de editor de texto.
Implementación básica
Ejemplo
"""Clase Memento - guarda el estado del editor de texto"""
def __init__(self, content):
self._content = content
def get_content(self):
"""Obtener el contenido guardado"""
return self._content
class TextEditor:
"""Clase Originator - editor de texto"""
def __init__(self):
self._content = ""
def write(self, text):
"""Escribir texto"""
self._content += text
def get_content(self):
"""Obtener el contenido actual"""
return self._content
def save(self):
"""Crear memento - guardar el estado actual"""
return TextEditorMemento(self._content)
def restore(self, memento):
"""Restaurar estado - leer desde el memento"""
self._content = memento.get_content()
class HistoryManager:
"""Clase Administrador - administrador de historial"""
def __init__(self):
self._mementos = []
def push(self, memento):
"""Guardar memento"""
self._mementos.append(memento)
def pop(self):
"""Obtener el último memento"""
if self._mementos:
return self._mementos.pop()
return None
Código de ejemplo completo
Ejemplo
def memo_pattern_demo():
# Crear el editor de texto y el administrador de historial
editor = TextEditor()
history = HistoryManager()
print("=== Demostración del patrón Memento del editor de texto ==="\n")
# Primera edición y guardar
editor.write("Hello, ")
history.push(editor.save())
print(f"Contenido actual: {editor.get_content()}")
print("✅ Estado guardado\n")
# Segunda edición y guardar
editor.write("World!")
history.push(editor.save())
print(f"Contenido actual: {editor.get_content()}")
print("✅ Estado guardado\n")
# Tercera edición sin guardar
editor.write(" This is new text.")
print(f"Contenido actual: {editor.get_content()}")
print("❌ Edición actual no guardada\n")
# Operación de deshacer - restaurar al estado guardado anterior
memento = history.pop()
if memento:
editor.restore(memento)
print(f"Contenido después de deshacer: {editor.get_content()}")
print("↩️ Deshecho al punto de guardado anterior\n")
# Deshacer de nuevo
memento = history.pop()
if memento:
editor.restore(memento)
print(f"Contenido después de deshacer de nuevo: {editor.get_content()}")
print("↩️ Deshecho al estado inicial")
# Ejecutar la demo
if __name__ == "__main__":
memo_pattern_demo()
Resultado de la ejecución:

Aplicación avanzada: Memento que admite múltiples estados
En aplicaciones reales, puede que necesitemos guardar múltiples atributos de un objeto. A continuación se muestra un ejemplo más complejo:
Ejemplo
import json
class GameStateMemento:
"""Memento del estado del juego"""
def __init__(self, level, score, player_health, inventory):
self._state = {
'level': level,
'score': score,
'player_health': player_health,
'inventory': inventory.copy(), # Crear una copia para evitar problemas de referencia
'timestamp': datetime.datetime.now().isoformat()
}
def get_state(self):
"""Obtener el estado completo"""
return self._state.copy()
def get_timestamp(self):
"""Obtener el tiempo de guardado"""
return self._state['timestamp']
class Game:
"""Clase de juego - Originador"""
def __init__(self):
self.level = 1
self.score = 0
self.player_health = 100
self.inventory = ['sword', 'potion']
def play(self, level_increase=1, score_increase=100, health_change=0, new_item=None):
"""Simular la partida"""
self.level += level_increase
self.score += score_increase
self.player_health += health_change
if new_item and new_item not in self.inventory:
self.inventory.append(new_item)
# La vida no puede ser negativa
self.player_health = max(0, self.player_health)
def display_status(self):
"""Mostrar el estado actual"""
status = f"""
🎮 Estado del juego:
Nivel: {self.level}
Puntuación: {self.score}
Vida: {self.player_health}
Inventario: {', '.join(self.inventory)}
"""
print(status)
def save_game(self):
"""Guardar el estado del juego"""
return GameStateMemento(
self.level,
self.score,
self.player_health,
self.inventory
)
def load_game(self, memento):
"""Cargar el estado del juego"""
state = memento.get_state()
self.level = state['level']
self.score = state['score']
self.player_health = state['player_health']
self.inventory = state['inventory']
class SaveManager:
"""Gestor de guardados"""
def __init__(self):
self.saves = {}
def create_save(self, save_name, memento):
"""Crear guardado"""
self.saves[save_name] = memento
print(f💾 ¡Partida guardada '{save_name}' creada con éxito!)
def load_save(self, save_name):
Cargar partida guardada
if save_name in self.saves:
print(f🔄 Cargando partida guardada '{save_name}'...)
return self.saves[save_name]
else:
print(f❌ ¡La partida guardada '{save_name}' no existe!)
return None
def list_saves(self):
Listar todas las partidas guardadas
if not self.saves:
print(📁 No hay partidas guardadas)
return
print("\n📋 Lista de partidas guardadas:)
for name, memento in self.saves.items():
timestamp = memento.get_timestamp()
print(f- {name} (guardada en: {timestamp}))
# Demostración de ejemplo avanzado
def advanced_memo_demo():
print(=== Demostración del sistema de guardado ===\n")
game = Game()
save_manager = SaveManager()
# Estado inicial
print(🎯 Iniciar nuevo juego:)
game.display_status()
# Progreso del juego
print(🚀 Jugando...)
game.play(level_increase=2, score_increase=500, new_item='shield')
game.display_status()
# Guardar primera partida
save_manager.create_save(Primer nivel completado, game.save_game())
# Continuar partida
print(⚔️ Continuando aventura...)
game.play(level_increase=1, score_increase=200, health_change=-30, new_item='magic_wand')
game.display_status()
# Guardar segunda partida
save_manager.create_save(Inicio del segundo nivel, game.save_game())
# Mostrar lista de partidas guardadas
save_manager.list_saves()
# Cargar la primera partida guardada
print("\n⏪ Volver al estado del primer nivel superado:)
first_save = save_manager.load_save(Primer nivel completado)
if first_save:
game.load_game(first_save)
game.display_status()
# Ejecutar ejemplo avanzado
if __name__ == "__main__":
advanced_memo_demo()
Ventajas y desventajas del patrón Memento
Ventajas
- Protección de encapsulación: no expone el estado interno del objeto, mantiene la encapsulación
- Simplica el originador: separa la lógica de guardado de estado de la lógica de negocio
- Fácil de implementar la restauración: proporciona un mecanismo simple de restauración de estado
- Soporta múltiples deshaceres: puede guardar el estado en múltiples momentos
Desventajas
- Consumo de memoria: si el estado es muy grande o se guarda con frecuencia, consumirá mucha memoria
- Sobrecarga del administrador: requiere clases adicionales para administrar los mementos
- Gestión del ciclo de vida: es necesario manejar adecuadamente la creación y destrucción de los mementos
Mejores prácticas y consideraciones
1. Estrategias de optimización de memoria
Ejemplo
class LimitedHistoryManager:
def __init__(self, max_size=10):
self._mementos = []
self._max_size = max_size
def push(self, memento):
if len(self._mementos) >= self._max_size:
# Eliminar el registro más antiguo
self._mementos.pop(0)
self._mementos.append(memento)
2. Manejo de estados de objetos complejos
Para objetos que contienen referencias complejas, es necesario asegurarse de que el memento cree una copia profunda:
Ejemplo
class ComplexMemento:
def __init__(self, complex_state):
# Usar copia profunda para evitar problemas de referencia
self._state = copy.deepcopy(complex_state)
3. Guardado selectivo
No todos los estados necesitan ser guardados; se puede optar por guardar solo las propiedades importantes:
Ejemplo
"""Guardar selectivamente el estado importante"""
important_state = {
'essential_data': self.essential_data,
'critical_settings': self.critical_settings
# Ignorar datos temporales y cachés
}
return SelectiveMemento(important_state)
Escenarios de aplicación real
1. Editor de gráficos
Ejemplo
def save_state(self):
return GraphicMemento(
self.selected_shapes.copy(),
self.canvas_state,
self.view_settings
)
2. Administrador de configuración
Ejemplo
def backup_config(self):
return ConfigMemento(
self.current_settings.copy(),
self.user_preferences
)
3. Operaciones transaccionales
Ejemplo
def create_checkpoint(self):
return TransactionMemento(
self.connection_state,
self.pending_operations.copy()
)
Resumen
El patrón Memento es un patrón de diseño potente y práctico que nos proporciona un elegante mecanismo de guardado y restauración de estados. Al separar la lógica de guardado de estados de la lógica de negocio, no solo mantiene la limpieza del código, sino que también mejora la mantenibilidad del sistema.
Puntos clave:
- El núcleo del patrón Memento es el mecanismo de "guardar-restaurar"
- Los tres roles principales cumplen cada uno sus funciones con responsabilidades claras
- En el uso práctico, hay que prestar atención a la gestión de memoria y a la selección de estados
- Es adecuado para escenarios que requieren deshacer, rehacer o revertir estados