Memoization Pattern: State Persistence & Restoration
The Memento Pattern is a behavioral pattern used to save the internal state of an object and restore it later when needed. In this pattern, a memento object encapsulates the internal state of another object for preservation purposes, enabling its restoration at any point in time. Typically within the context of the Memento Pattern, the memento objects are managed by a caretaker object. The caretaker holds references to these mementos and stores them within a history log. When it is necessary to restore an object's state, the caretaker retrieves a specific memento from its history log and applies that saved state back to the original object.
The implementation of the Memento Pattern generally involves four distinct roles:
- Originator: Defines methods for saving the current state of an object and restoring it to a previous state.
- Memento: Responsible for encapsulating the internal data of an object, containing either its full or partial state information.
- Caretaker: Manages memento objects and maintains a history log used to store these snapshots.
- Client: Creates and configures both the Originator and Caretaker instances, then executes specific operations involving them.
The core concept of this pattern revolves around preserving an object's internal state so that it can be restored upon demand. The Memento Pattern is particularly suitable for scenarios such as:
- Saving and restoring object states to implement features like Undo (撤销) and Redo.
- Performing snapshotting and restoration operations, commonly seen in database transactions.
By encapsulating an object's state within a memento object, the Memento Pattern enhances system flexibility and scalability while allowing for arbitrary point-in-time recovery of that state. Furthermore, it facilitates the implementation of Undo/Redo functionality by maintaining a chronological history of states. However, this pattern does introduce potential overhead regarding memory consumption, as multiple memento objects must be retained to support various restoration points.
class Memento:
"""Encapsulates the internal data of an object."""
def __init__(self, originator):
# Store a deep copy or reference of the Originator's state here
self._state = originator.get_state()
def get_data(self) -> dict:
return self._state
class Caretaker:
"""Manages memento objects and maintains history."""
def __init__(self):
# List to store all saved snapshots in chronological order
self.history = []
def add_memento(self, memento: Memento) -> None:
"""Adds a new snapshot to the history log"""
self.history.append(memento.get_data())
class Originator:
"""Creates and manages its own state."""
def __init__(self):
# Initial configuration of the object's data structure
self._data = {}
def set_state(self, new_state) -> None:
"""Updates the internal state"""
self._data = new_state
def get_state(self) -> dict:
return self._state.copy() # Return a copy to ensure immutability
# Example usage demonstrating the pattern in action
if __name__ == "__main__":
originator = Originator()
caretaker = Caretaker()
client = None
def create_client(origin, care): return Client(origin, care)
# Step 1: Create and configure objects (Client role simulation)
client = create_client(originator, caretaker)
# Step 2: Save initial state
originator.set_state({"action": "init", "value": 0})
memento_1 = Memento(originator)
caretaker.add_memento(memento_1)
print("Initial State:", originator.get_state())
# Step 3: Modify the state (e.g., user performs an action)
originator.set_state({"action": "update", "value": 42})
memento_2 = Memento(originator)
caretaker.add_memento(memento_2)
print("Current State:", originator.get_state())
# Step 4: Restore previous state (e.g., Undo operation)
restored_data = caretaker.history.pop() # Retrieve last memento data
if restored_data is not None:
temp_originator = Originator()
temp_originator._data = restored_data
originator.set_state(temp_originator.get_state())
print("Restored State:", originator.get_state())
class Client:
"""Coordinates between the Originator and Caretaker."""
def __init__(self, originator: Originator, caretaker: Caretaker):
self.originator = originator
self.caretaker = caretaker
def save(self) -> None:
memento = Memento(self.originator)
self.caretaker.add_memento(memento)
def undo(self) -> bool:
"""Attempts to restore the state"""
if len(self.caretaker.history) > 0:
data_to_restore = self.caretaker.history.pop()
# Reconstruct a temporary originator with restored state logic
temp_originator = Originator()
temp_originator._data = data_to_restore
return True
return False
def redo(self) -> bool:
"""Restores the most recently undone action"""
if len(self.caretaker.history) > 0:
# In a real implementation, we might need to re-add mementos
# that were popped during undo. Here we simulate retrieval logic.
data_to_restore = self.caretaker.history.pop()
temp_originator = Originator()
temp_originator._data = data_to_restore
return True
return False
def execute_action(self, new_state) -> None:
"""Performs an action that modifies the originator's state"""
self.originator.set_state(new_state)