Design patterns: Memento
The Memento Pattern is a behavioral pattern that lets you capture and save an object’s internal state without violating its encapsulation, so that the saved state can be restored later. This pattern is useful when you want to implement an undo/redo system or keep track of an object’s past states.
Strengths
- Saving and Restoring State: It lets you store an object’s internal state and restore it later, providing undo/redo functionality simply and effectively.
- State Encapsulation: It preserves the object’s encapsulation, because the memento doesn’t reveal the implementation details or internal state of the object it is associated with.
- Simple Handling of Complex States: It makes it easier to manage objects with complex states, allowing you to move easily between previous or subsequent states.
- Separation of Concerns: The Memento Pattern separates the responsibility of storing state (handled by the memento) from the business logic of the main object (handled by the originator).
- Client Independence: The client doesn’t need to know the details of how states are saved and restored. It only has to handle the memento as an opaque object.
Use Case
Imagine a text editor that lets you undo changes (the “undo” feature) thanks to the Memento Pattern.
PHP Code
//`Editor` class (Originator)
class Editor {
private $content;
public function setContent($content) {
$this->content = $content;
}
public function getContent() {
return $this->content;
}
// Create a memento that stores the current state
public function save() {
return new EditorMemento($this->content);
}
// Restore the state saved in a memento
public function restore(EditorMemento $memento) {
$this->content = $memento->getContent();
}
}
//`Editor` class (Originator)
class EditorMemento {
private $content;
public function __construct($content) {
$this->content = $content;
}
public function getContent() {
return $this->content;
}
}
// History class (Caretaker)
class History {
private $mementos = [];
// Add a memento to the history
public function push(EditorMemento $memento) {
$this->mementos[] = $memento;
}
// Restore the last saved state
public function pop() {
if (!empty($this->mementos)) {
return array_pop($this->mementos);
}
return null;
}
}
// Using the Memento Pattern
$editor = new Editor();
$history = new History();
// First version of the content
$editor->setContent("First version of the text.");
$history->push($editor->save());
// Second version of the content
$editor->setContent("Second version of the text.");
$history->push($editor->save());
// Third version of the content
$editor->setContent("Third version of the text.");
echo "Current content: " . $editor->getContent() . PHP_EOL;
// Restore the last saved version
$editor->restore($history->pop());
echo "Content after undo: " . $editor->getContent() . PHP_EOL;
// Restore the version before that
$editor->restore($history->pop());
echo "Content after another undo: " . $editor->getContent() . PHP_EOL;
/* Output
Current content: Third version of the text.
Content after undo: Second version of the text.
Content after another undo: First version of the text.
*/
Conclusion
The Memento Pattern is ideal when you need to keep track of an object’s previous states, as in undo/redo systems. It upholds the principle of encapsulation while simplifying the management of complex states, without exposing implementation details to the other components of the system.