Introduction
The Memento pattern is a behavioral design pattern that allows an object to save and restore its state without revealing its implementation details. This pattern is particularly useful for implementing undo mechanisms in applications.
Key Concepts
- Originator: The object whose state needs to be saved and restored.
- Memento: The object that stores the state of the Originator.
- Caretaker: The object that requests the save and restore operations from the Originator.
Structure
The Memento pattern involves three main components:
- Originator: Creates a memento containing a snapshot of its current internal state and uses the memento to restore its internal state.
- Memento: Stores the internal state of the Originator object. The Memento protects against access by objects other than the Originator.
- Caretaker: Responsible for keeping the memento. The Caretaker never operates on or examines the contents of a memento.
UML Diagram
+----------------+ +----------------+ +----------------+ | Originator | | Memento | | Caretaker | +----------------+ +----------------+ +----------------+ | - state | | - state | | - memento | +----------------+ +----------------+ +----------------+ | + createMemento() | + getState() | | + saveState() | | + setMemento(memento) | + setState() | | + restoreState()| +----------------+ +----------------+ +----------------+
Example
Let's implement a simple example in Python to demonstrate the Memento pattern. We will create a text editor that can save and restore its state.
Code Implementation
Originator
class TextEditor: def __init__(self): self._content = "" def type(self, words): self._content += words def get_content(self): return self._content def save(self): return Memento(self._content) def restore(self, memento): self._content = memento.get_content()
Memento
class Memento: def __init__(self, content): self._content = content def get_content(self): return self._content
Caretaker
class Caretaker: def __init__(self, editor): self._editor = editor self._history = [] def save(self): self._history.append(self._editor.save()) def undo(self): if not self._history: return memento = self._history.pop() self._editor.restore(memento)
Usage
if __name__ == "__main__": editor = TextEditor() caretaker = Caretaker(editor) editor.type("Hello, ") caretaker.save() editor.type("world!") caretaker.save() editor.type(" How are you?") print("Current content:", editor.get_content()) caretaker.undo() print("After undo:", editor.get_content()) caretaker.undo() print("After second undo:", editor.get_content())
Output
Practical Exercises
Exercise 1: Implement a Calculator with Undo Functionality
Task: Implement a simple calculator that supports addition and subtraction. The calculator should be able to save its state and undo the last operation.
Solution:
Calculator (Originator)
class Calculator: def __init__(self): self._value = 0 def add(self, number): self._value += number def subtract(self, number): self._value -= number def get_value(self): return self._value def save(self): return Memento(self._value) def restore(self, memento): self._value = memento.get_value()
Memento
class Memento: def __init__(self, value): self._value = value def get_value(self): return self._value
Caretaker
class Caretaker: def __init__(self, calculator): self._calculator = calculator self._history = [] def save(self): self._history.append(self._calculator.save()) def undo(self): if not self._history: return memento = self._history.pop() self._calculator.restore(memento)
Usage
if __name__ == "__main__": calculator = Calculator() caretaker = Caretaker(calculator) calculator.add(10) caretaker.save() calculator.add(5) caretaker.save() calculator.subtract(3) print("Current value:", calculator.get_value()) caretaker.undo() print("After undo:", calculator.get_value()) caretaker.undo() print("After second undo:", calculator.get_value())
Output
Common Mistakes and Tips
- Not Saving State Frequently: Ensure you save the state at appropriate times to avoid losing important changes.
- Excessive Memory Usage: Be mindful of the memory usage when saving states, especially for large objects.
- Restoring Incorrect State: Always ensure that the memento being restored is valid and corresponds to the correct state.
Conclusion
The Memento pattern is a powerful tool for managing the state of an object, especially when implementing undo functionality. By separating the state-saving logic from the main business logic, it promotes cleaner and more maintainable code. In the next section, we will explore the Observer pattern, which allows objects to notify other objects about changes in their state.
Software Design Patterns Course
Module 1: Introduction to Design Patterns
- What are Design Patterns?
- History and Origin of Design Patterns
- Classification of Design Patterns
- Advantages and Disadvantages of Using Design Patterns
Module 2: Creational Patterns
Module 3: Structural Patterns
Module 4: Behavioral Patterns
- Introduction to Behavioral Patterns
- Chain of Responsibility
- Command
- Interpreter
- Iterator
- Mediator
- Memento
- Observer
- State
- Strategy
- Template Method
- Visitor
Module 5: Application of Design Patterns
- How to Select the Right Pattern
- Practical Examples of Pattern Usage
- Design Patterns in Real Projects
- Refactoring Using Design Patterns
Module 6: Advanced Design Patterns
- Design Patterns in Modern Architectures
- Design Patterns in Microservices
- Design Patterns in Distributed Systems
- Design Patterns in Agile Development