Python Memento
## Python Memento Pattern | Rookie Tutorial
The Memento pattern is a behavioral design pattern that allows capturing and saving an object's internal state without breaking encapsulation, so that the object can be restored to a previous state when needed.
Imagine you're playing a game that provides a "save" feature. You can save your current progress at any time, and if you fail later or want to retry a level, you can load the previous save. The Memento pattern is the programming solution that implements this "save-load" functionality.
* * *
## Why Do We Need the Memento Pattern?
### Application Scenarios
* **Text Editors**: Implementing Undo and Redo functionality
* **Game Development**: Saving game progress and state
* **Graphics Software**: Supporting operation history
* **Transaction Processing**: Rolling back to previous states when operations fail
### Problems Solved
* Need to save object state without exposing internal details
* Provide state recovery mechanism with "regret" functionality
* Maintain object encapsulation and avoid mixing state saving logic with business logic
* * *
## Core Components of the Memento Pattern
The Memento pattern consists of three main roles:
### 1. Originator
* The object whose state needs to be saved
* Creates mementos to record current state
* Uses mementos to restore previous states
### 2. Memento
* Stores the internal state of the originator object
* Prevents objects other than the originator from accessing the memento's content
### 3. Caretaker
* Responsible for saving mementos
* Cannot operate on or inspect the content of mementos
!(#)
* * *
Let's implement the Memento pattern through a text editor example.
### Basic Implementation
## Example
class TextEditorMemento:
"""Memento class - saves text editor state"""
def __init__ (self, content):
self._content = content
def get_content(self):
"""Get saved content"""
return self._content
class TextEditor:
"""Originator class - text editor"""
def __init__ (self):
self._content =""
def write(self, text):
"""Write text"""
self._content += text
def get_content(self):
"""Get current content"""
return self._content
def save(self):
"""Create memento - save current state"""
return TextEditorMemento(self._content)
def restore(self, memento):
"""Restore state - read from memento"""
self._content = memento.get_content()
class HistoryManager:
"""Caretaker class - history manager"""
def __init__ (self):
self._mementos =[]
def push(self, memento):
"""Save memento"""
self._mementos.append(memento)
def pop(self):
"""Get latest memento"""
if self._mementos:
return self._mementos.pop()
return None
### Complete Example Code
## Example
# Complete Memento pattern example
def memo_pattern_demo():
# Create text editor and history manager
editor = TextEditor()
history = HistoryManager()
print("=== Text Editor Memento Pattern Demo ===n")
# First edit and save
editor.write("Hello, ")
history.push(editor.save())
print(f"Current content: {editor.get_content()}")
print("✅ State savedn")
# Second edit and save
editor.write("World!")
history.push(editor.save())
print(f"Current content: {editor.get_content()}")
print("✅ State savedn")
# Third edit without saving
editor.write(" This is new text.")
print(f"Current content: {editor.get_content()}")
print("❌ Current edit not savedn")
# Undo operation - restore to last saved state
memento = history.pop()
if memento:
editor.restore(memento)
print(f"Content after undo: {editor.get_content()}")
print("↩️ Undone to previous save pointn")
# Undo again
memento = history.pop()
if memento:
editor.restore(memento)
print(f"Content after second undo: {editor.get_content()}")
print("↩️ Undone to initial state")
# Run demo
if __name__ =="__main__":
memo_pattern_demo()
**Execution Result:**
!(#)
* * *
## Advanced Application: Memento Supporting Multiple States
In practical applications, we may need to save multiple attributes of an object. Here's a more complex example:
## Example
import datetime
import json
class GameStateMemento:
"""Game state memento"""
def __init__ (self, level, score, player_health, inventory):
self._state ={
'level': level,
'score': score,
'player_health': player_health,
'inventory': inventory.copy(),# Create copy to avoid reference issues
'timestamp': datetime.datetime.now().isoformat()
}
def get_state(self):
"""Get complete state"""
return self._state.copy()
def get_timestamp(self):
"""Get save time"""
return self._state['timestamp']
class Game:
"""Game class - 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):
"""Simulate game progress"""
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)
# Health cannot be negative
self.player_health=max(0,self.player_health)
def display_status(self):
"""Display current status"""
status = f"""
🎮 Game Status:
Level: {self.level}
Score: {self.score}
Health: {self.player_health}
Inventory: {', '.join(self.inventory)}
"""
print(status)
def save_game(self):
"""Save game state"""
return GameStateMemento(
self.level,
self.score,
self.player_health,
self.inventory
)
def load_game(self, memento):
"""Load game state"""
state = memento.get_state()
self.level= state['level']
self.score= state['score']
self.player_health= state['player_health']
self.inventory= state['inventory']
class SaveManager:
"""Save manager"""
def __init__ (self):
self.saves={}
def create_save(self, save_name, memento):
"""Create save"""
self.saves= memento
print(f"💾 Save '{save_name}' created successfully!")
def load_save(self, save_name):
"""Load save"""
if save_name in self.saves:
print(f"🔄 Loading save '{save_name}'...")
return self.saves
else:
print(f"❌ Save '{save_name}' does not exist!")
return None
def list_saves(self):
"""List all saves"""
if not self.saves:
print("📁 No saves available")
return
print("n📋 Save List:")
for name, memento in self.saves.items():
timestamp = memento.get_timestamp()
print(f" - {name} (Saved at: {timestamp})")
# Advanced example demo
def advanced_memo_demo():
print("=== Game Save System Demo ===n")
game = Game()
save_manager = SaveManager()
# Initial state
print("🎯 Starting new game:")
game.display_status()
# Game progress
print("🚀 Playing game...")
game.play(level_increase=2, score_increase=500, new_item='shield')
game.display_status()
# Save first checkpoint
save_manager.create_save("Level 1 Complete", game.save_game())
# Continue playing
print("⚔️ Continuing adventure...")
game.play(level_increase=1, score_increase=200, health_change=-30, new_item='magic_wand')
game.display_status()
# Save second checkpoint
save_manager.create_save("Level 2 Start", game.save_game())
# Display save list
save_manager.list_saves()
# Load first save
print("n⏪ Returning to Level 1 Complete state:")
first_save = save_manager.load_save("Level 1 Complete")
if first_save:
game.load_game(first_save)
game.display_status()
# Run advanced example
if __name__ =="__main__":
advanced_memo_demo()
* * *
## Pros and Cons of the Memento Pattern
### Advantages
1. **Encapsulation Protection**: Does not expose object internal state, maintains encapsulation
2. **Simplifies Originator**: Separates state saving logic from business logic
3. **Easy Recovery**: Provides simple state recovery mechanism
4. **Supports Multiple Undos**: Can save states at multiple time points
### Disadvantages
1. **Memory Consumption**: If states are large or saved frequently, consumes significant memory
2. **Caretaker Overhead**: Requires additional class to manage mementos
3. **Lifecycle Management**: Needs proper handling of memento creation and destruction
* * *
## Best Practices and Considerations
### 1. Memory Optimization Strategies
## Example
# Limit the number of saved history records
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:
# Remove oldest record
self._mementos.pop(0)
self._mementos.append(memento)
### 2. Handling Complex Object States
For objects containing complex references, ensure mementos create deep copies:
## Example
import copy
class ComplexMemento:
def __init__ (self, complex_state):
# Use deep copy to avoid reference issues
self._state =copy.deepcopy(complex_state)
### 3. Selective Saving
Not all states need to be saved; can choose to save only important attributes:
## Example
def create_selective_memento(self):
"""Selectively save important states"""
important_state ={
'essential_data': self.essential_data,
'critical_settings': self.critical_settings
# Ignore temporary data and cache
}
return SelectiveMemento(important_state)
* * *
## Real-World Application Scenarios
### 1. Graphics Editor
## Example
class GraphicEditor:
def save_state(self):
return GraphicMemento(
self.selected_shapes.copy(),
self.canvas_state,
self.view_settings
)
### 2. Configuration Manager
## Example
class ConfigManager:
def backup_config(self):
return ConfigMemento(
self.current_settings.copy(),
self.user_preferences
)
### 3. Transaction Operations
## Example
class DatabaseTransaction:
def create_checkpoint(self):
return TransactionMemento(
self.connection_state,
self.pending_operations.copy()
)
* * *
## Summary
The Memento pattern is a powerful and practical design pattern that provides us with an elegant state saving and recovery mechanism. By separating state saving logic from business logic, it not only keeps code clean but also enhances system maintainability.
**Key Points:**
* The core of Memento pattern is the "save-restore" mechanism
* Three core roles with clear and distinct responsibilities
* Pay attention to memory management and state selection in practical use
* Suitable for scenarios requiring undo, redo, or state rollback
YouTip