Python-Memento-Muster (Memento)
Das Memento-Muster ist ein Verhaltens-Entwurfsmuster, das den internen Zustand eines Objekts erfassen und speichern kann, ohne die Kapselung zu verletzen, sodass es bei Bedarf in einen früheren Zustand wiederhergestellt werden kann.
Stellen Sie sich vor, Sie spielen ein Spiel, das eine "Speichern"-Funktion bietet. Sie können den aktuellen Spielfortschritt jederzeit speichern. Wenn Sie später scheitern oder ein Level erneut versuchen möchten, können Sie einen früheren Speicherstand laden. Das Memento-Muster ist eine Programmierlösung, die diese "Speichern-Laden"-Funktionalität implementiert.
Warum brauchen wir das Memento-Muster?
Anwendungsszenarien
- Texteditor: Rückgängig- und Wiederholen-Funktionalität implementieren
- Spieleentwicklung: Spielfortschritt und -zustand speichern
- Grafiksoftware: Operationsverlauf unterstützen
- Transaktionsverarbeitung: Bei fehlgeschlagener Operation auf einen früheren Zustand zurückrollen
Gelöste Probleme
- Objektzustand speichern müssen, ohne interne Details offenzulegen
- Einen Mechanismus zur Zustandswiederherstellung bereitstellen, der eine "Reue-Medizin"-Funktion (d. h. die Möglichkeit zum Rückgängigmachen) unterstützt
- Die Objektkapselung erhalten und vermeiden, Logik zum Speichern von Zuständen mit Geschäftslogik zu vermischen
Kernkomponenten des Memento-Musters
Das Memento-Muster besteht aus drei Hauptrollen:
1. Originator
- Das Objekt, das den Zustand speichern muss
- Erstellt ein Memento, um den aktuellen Zustand aufzuzeichnen
- Verwendet das Memento, um einen vorherigen Zustand wiederherzustellen
2. Memento
- Speichert den internen Zustand des Originator-Objekts
- Verhindert, dass andere Objekte als der Originator auf den Inhalt des Mementos zugreifen
3. Caretaker
- Für das Speichern von Mementos verantwortlich
- Kann den Inhalt von Mementos nicht bearbeiten oder einsehen

Python-Implementierung des Memento-Musters
Lass uns das Memento-Muster konkret anhand eines Texteditor-Beispiels implementieren.
Grundlegende Implementierung
Beispiel
"""Memento-Klasse - speichert den Zustand des Texteditors"""
def __init__(self, content):
self._content = content
def get_content(self):
"""Den gespeicherten Inhalt abrufen"""
return self._content
class TextEditor:
"""Originator-Klasse - Texteditor"""
def __init__(self):
self._content = ""
def write(self, text):
"""Text schreiben"""
self._content += text
def get_content(self):
"""Aktuellen Inhalt abrufen"""
return self._content
def save(self):
"""Memento erstellen - aktuellen Zustand speichern"""
return TextEditorMemento(self._content)
def restore(self, memento):
"""Zustand wiederherstellen - aus Memento lesen"""
self._content = memento.get_content()
class HistoryManager:
"""Caretaker-Klasse - Verlaufsmanager"""
def __init__(self):
self._mementos = []
def push(self, memento):
"""Memento speichern"""
self._mementos.append(memento)
def pop(self):
"""Neuestes Memento abrufen"""
if self._mementos:
return self._mementos.pop()
return None
Vollständiger Beispielcode
Beispiel
def memo_pattern_demo():
# Texteditor und Verlaufsmanager erstellen
editor = TextEditor()
history = HistoryManager()
print("=== Texteditor Memento-Muster-Demo ==="\n")
# Erste Bearbeitung und Speichern
editor.write("Hallo, ")
history.push(editor.save())
print(f"Aktueller Inhalt: {editor.get_content()}")
print("✅ Zustand gespeichert"\n")
# Zweite Bearbeitung und Speichern
editor.write("Welt!")
history.push(editor.save())
print(f"Aktueller Inhalt: {editor.get_content()}")
print("✅ Zustand gespeichert"\n")
# Dritte Bearbeitung, aber nicht gespeichert
editor.write(" Dies ist neuer Text.")
print(f"Aktueller Inhalt: {editor.get_content()}")
print("❌ Aktuelle Bearbeitung nicht gespeichert"\n")
# Rückgängig-Operation - zum zuletzt gespeicherten Zustand wiederherstellen
memento = history.pop()
if memento:
editor.restore(memento)
print(f"Inhalt nach Rückgängig: {editor.get_content()}")
print("↩️ Zum vorherigen Speicherpunkt rückgängig gemacht\n")
# Erneut rückgängig machen
memento = history.pop()
if memento:
editor.restore(memento)
print(f"Inhalt nach erneutem Rückgängigmachen: {editor.get_content()}")
print("↩️ Zum Anfangszustand rückgängig gemacht")
# Demo ausführen
if __name__ == "__main__":
memo_pattern_demo()
Ausführungsergebnis:

Fortgeschrittene Anwendung: Memento zur Unterstützung mehrerer Zustände
In echten Anwendungen müssen wir möglicherweise mehrere Attribute eines Objekts speichern. Hier ist ein komplexeres Beispiel:
Beispiel
import json
class GameStateMemento:
"""Spielzustand-Memento"""
def __init__(self, level, score, player_health, inventory):
self._state = {
'level': level,
'score': score,
'player_health': player_health,
'inventory': inventory.copy(), # Erstelle eine Kopie, um Referenzprobleme zu vermeiden
'timestamp': datetime.datetime.now().isoformat()
}
def get_state(self):
"""Vollständigen Zustand abrufen"""
return self._state.copy()
def get_timestamp(self):
"""Speicherzeit abrufen"""
return self._state['timestamp']
class Game:
"""Game-Klasse - Originator"""
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):
"""Simuliert den Spielablauf"""
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)
# Gesundheit kann nicht negativ sein
self.player_health = max(0, self.player_health)
def display_status(self):
"""Aktuellen Zustand anzeigen"""
status = f"""
🎮 Spielzustand:
Level: {self.level}
Punktzahl: {self.score}
Gesundheit: {self.player_health}
Inventar: {', '.join(self.inventory)}
"""
print(status)
def save_game(self):
"""Spielzustand speichern"""
return GameStateMemento(
self.level,
self.score,
self.player_health,
self.inventory
)
def load_game(self, memento):
"""Spielzustand laden"""
state = memento.get_state()
self.level = state['level']
self.score = state['score']
self.player_health = state['player_health']
self.inventory = state['inventory']
class SaveManager:
"""Spielstandverwalter"""
def __init__(self):
self.saves = {}
def create_save(self, save_name, memento):
"""Spielstand erstellen"""
self.saves[save_name] = memento
print(f"💾 Spielstand '{save_name}' erfolgreich erstellt!")
def load_save(self, save_name):
"""Spielstand laden"""
if save_name in self.saves:
print(f"🔄 Lade Spielstand '{save_name}'...")
return self.saves[save_name]
else:
print(f"❌ Spielstand '{save_name}' existiert nicht!")
return None
def list_saves(self):
"""Alle Spielstände auflisten"""
if not self.saves:
print("📁 Keine Spielstände verfügbar")
return
print("\n📋 Spielstandliste:")
for name, memento in self.saves.items():
timestamp = memento.get_timestamp()
print(f" - {name} (Gespeichert am: {timestamp})")
# Erweitertes Beispiel-Demo
def advanced_memo_demo():
print("=== Spielstand-System-Demo ==="\n")
game = Game()
save_manager = SaveManager()
# Anfangszustand
print("🎯 Ein neues Spiel wird gestartet:")
game.display_status()
# Spielfortschritt
print("🚀 Das Spiel wird gespielt...")
game.play(level_increase=2, score_increase=500, new_item='shield')
game.display_status()
# Ersten Spielstand speichern
save_manager.create_save("Level 1 abgeschlossen", game.save_game())
# Das Spiel fortsetzen
print("⚔️ Abenteuer fortsetzen...")
game.play(level_increase=1, score_increase=200, health_change=-30, new_item='magic_wand')
game.display_status()
# Speichere den zweiten Spielstand
save_manager.create_save("Level 2 startet", game.save_game())
# Spielstandsliste anzeigen
save_manager.list_saves()
# Lade den ersten Spielstand
print("\n⏪ Zurück zum abgeschlossenen Status von Level 1:")
first_save = save_manager.load_save("Level 1 abgeschlossen")
if first_save:
game.load_game(first_save)
game.display_status()
# Führe das fortgeschrittene Beispiel aus
if __name__ == "__main__":
advanced_memo_demo()
Vor- und Nachteile des Memento-Musters
Vorteile
- Kapselungsschutz: Der interne Zustand eines Objekts wird nicht offengelegt, wodurch die Kapselung erhalten bleibt
- Vereinfachung des Originators: Trennt die Zustandsspeicherungslogik von der Geschäftslogik
- Einfach zu implementierende Wiederherstellung: Bietet einen einfachen Mechanismus zur Zustandswiederherstellung
- Unterstützung mehrerer Rückgängig-Schritte: Kann Zustände zu mehreren Zeitpunkten speichern
Nachteile
- Speicherverbrauch: Wenn der Zustand groß ist oder häufig gespeichert wird, wird viel Speicher verbraucht
- Caretaker-Overhead: Erfordert eine zusätzliche Klasse zur Verwaltung von Mementos
- Lebenszyklusverwaltung: Erstellung und Zerstörung von Mementos müssen ordnungsgemäß behandelt werden
Best Practices und Vorsichtsmaßnahmen
1. Speicheroptimierungsstrategie
Beispiel
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:
# Entferne den ältesten Eintrag
self._mementos.pop(0)
self._mementos.append(memento)
2. Umgang mit komplexen Objektzuständen
Stellen Sie bei Objekten mit komplexen Referenzen sicher, dass das Memento eine tiefe Kopie erstellt:
Beispiel
class ComplexMemento:
def __init__(self, complex_state):
# Verwenden Sie eine tiefe Kopie, um Referenzprobleme zu vermeiden
self._state = copy.deepcopy(complex_state)
3. Selektives Speichern
Nicht alle Zustände müssen gespeichert werden; Sie können auswählen, nur wichtige Eigenschaften zu speichern:
Beispiel
"""Wichtigen Zustand selektiv speichern"""
important_state = {
'essential_data': self.essential_data,
'critical_settings': self.critical_settings
# Temporäre Daten und Cache ignorieren
}
return SelectiveMemento(important_state)
Anwendungsszenarien aus der Praxis
1. Grafikeditor
Beispiel
def save_state(self):
return GraphicMemento(
self.selected_shapes.copy(),
self.canvas_state,
self.view_settings
)
2. Konfigurationsmanager
Beispiel
def backup_config(self):
return ConfigMemento(
self.current_settings.copy(),
self.user_preferences
)
3. Transaktionsoperationen
Beispiel
def create_checkpoint(self):
return TransactionMemento(
self.connection_state,
self.pending_operations.copy()
)
Zusammenfassung
Das Memento-Muster ist ein leistungsfähiges und praktisches Entwurfsmuster, das einen eleganten Mechanismus zum Speichern und Wiederherstellen von Zuständen bietet. Durch die Trennung der Zustandsspeicherungslogik von der Geschäftslogik bleibt der Code nicht nur sauber, sondern verbessert auch die Wartbarkeit des Systems.
Wichtige Punkte:
- Der Kern des Memento-Musters ist der "Speichern-Wiederherstellen"-Mechanismus.
- Die drei Kernrollen erfüllen jeweils ihre eigenen Aufgaben mit klaren Verantwortlichkeiten.
- In der Praxis ist auf Speicherverwaltung und Zustandsauswahl zu achten.
- Geeignet für Szenarien, die Rückgängigmachen, Wiederholen oder Zustands-Rollback erfordern.