Memento Pattern
Capture and restore object state without violating encapsulation
Pattern Overview
💾 Memento Pattern
The Memento Pattern captures and externalizes an object's internal state without violating encapsulation, allowing the object to be restored to this state later.
Core Concepts
🔹 Originator - Object whose state needs to be saved and restored
🔹 Memento - Stores the internal state of the originator
🔹 Caretaker - Manages mementos but never examines their contents
🔹 Encapsulation - Memento protects originator's state from external access
Real-World Applications
Text Editors - Undo/redo functionality with document state snapshots Games - Save/load game state at checkpoints or user request Configuration Management - Save/restore application settings Database Transactions - Rollback to previous state on failureState Management Benefits
Undo/Redo Operations - Navigate through state history efficiently Save Points - Create checkpoints for later restoration Rollback Capability - Revert to known good state after errors State Isolation - Memento contents remain opaque to caretakerImplementation Benefits
✅ Preserves encapsulation - No external access to object internals
✅ Simplified originator - No need to manage state history internally
✅ Flexible storage - Caretaker can implement any storage strategy
✅ State integrity - Immutable snapshots prevent accidental modification
Examples:
const { editor, history } = createTextEditorWithHistory();
editor.type('Hello');
history.save(editor.save());
editor.type(' World');
history.save(editor.save());
console.log('Current:', editor.getContent());
editor.restore(history.undo()!);
console.log('After undo:', editor.getContent());
editor.restore(history.redo()!);
console.log('After redo:', editor.getContent());Current: Hello World
After undo: Hello
After redo: Hello Worldconst { game, saveManager } = createGameWithSaveSystem();
game.addScore(100);
game.addItem('sword');
game.move(10, 20);
// Save at checkpoint
saveManager.saveGame('checkpoint1', game.createSavePoint());
game.takeDamage(50);
game.addScore(50);
console.log('After damage:', game.getStats());
// Load checkpoint
game.loadSavePoint(saveManager.loadGame('checkpoint1')!);
console.log('After loading:', game.getStats());After damage: {level: 1, score: 150, health: 50, inventory: ['sword'], position: {x: 10, y: 20}}
After loading: {level: 1, score: 100, health: 100, inventory: ['sword'], position: {x: 10, y: 20}}const { config, history } = createConfigManagerWithHistory();
config.setSetting('theme', 'dark');
config.setSetting('fontSize', 16);
history.saveSnapshot(config.createSnapshot('dark-theme'));
config.setSetting('theme', 'high-contrast');
config.setSetting('fontSize', 18);
console.log('Current theme:', config.getSetting('theme'));
config.restoreSnapshot(history.getSnapshot('dark-theme')!);
console.log('Restored theme:', config.getSetting('theme'));Current theme: high-contrast
Restored theme: darkConcepts
Complexity Analysis
Implementation
text-editor
// Memento interface
interface TextEditorMemento {
getContent(): string;
getCursor(): number;
getTimestamp(): Date;
}
// Originator
class TextEditor {
private content = '';
private cursor = 0;
type(text: string): void {
const before = this.content.substring(0, this.cursor);
const after = this.content.substring(this.cursor);
this.content = before + text + after;
this.cursor += text.length;
}
delete(count: number = 1): void {
const before = this.content.substring(0, Math.max(0, this.cursor - count));
const after = this.content.substring(this.cursor);
this.content = before + after;
this.cursor = Math.max(0, this.cursor - count);
}
save(): TextEditorMemento {
return new ConcreteTextEditorMemento(this.content, this.cursor);
}
restore(memento: TextEditorMemento): void {
this.content = memento.getContent();
this.cursor = memento.getCursor();
}
}