You’re building a text editor with undo functionality. Users need to revert to previous document states, but exposing internal state would break encapsulation. The Memento pattern lets you save and restore object state without revealing implementation details, enabling undo/redo and checkpoints.
Problem
Consider a text editor that needs undo functionality:
public class TextEditor
{
private string _content = "";
public void SetContent(string content)
{
_content = content;
}
public string GetContent()
{
return _content;
}
// How do we save state for undo?
public void Undo()
{
// Need previous state, but how?
}
}
You could expose internal state:
public class TextEditor
{
public string Content { get; set; } // Breaking encapsulation!
}
But this breaks encapsulation and allows external code to modify state incorrectly. You need a way to save state without exposing it.
Solution
The Memento pattern lets you save and restore the previous state of an object without revealing the details of its implementation.
Definition: The Memento pattern lets you save and restore the previous state of an object without revealing the details of its implementation.
The pattern has three components:
- Originator: The object whose state needs to be saved
- Memento: Stores the internal state of the originator
- Caretaker: Manages and stores mementos
Pseudocode
Originator (TextEditor)
↓ creates/restores
Memento (EditorMemento)
↓ stored/managed by
Caretaker (History)
Examples
Here’s a complete, runnable C# implementation:
using System;
using System.Collections.Generic;
// Memento
public class EditorMemento
{
public string Content { get; private set; }
public EditorMemento(string content)
{
Content = content;
}
}
// Originator
public class TextEditor
{
private string _content = "";
public void SetContent(string content)
{
_content = content;
Console.WriteLine($"Content set to: {_content}");
}
public string GetContent()
{
return _content;
}
public EditorMemento Save()
{
Console.WriteLine("Saving state...");
return new EditorMemento(_content);
}
public void Restore(EditorMemento memento)
{
_content = memento.Content;
Console.WriteLine($"Restored to: {_content}");
}
}
// Caretaker
public class History
{
private Stack<EditorMemento> _history = new Stack<EditorMemento>();
public void Save(EditorMemento memento)
{
_history.Push(memento);
}
public EditorMemento Undo()
{
if (_history.Count > 0)
{
return _history.Pop();
}
return null;
}
}
// Usage
class Program
{
static void Main()
{
var editor = new TextEditor();
var history = new History();
// Edit content
editor.SetContent("Hello");
history.Save(editor.Save());
editor.SetContent("Hello World");
history.Save(editor.Save());
editor.SetContent("Hello World!");
history.Save(editor.Save());
// Undo
Console.WriteLine("\nUndoing...");
var memento = history.Undo();
if (memento != null)
{
editor.Restore(memento);
}
Console.WriteLine("\nUndoing again...");
memento = history.Undo();
if (memento != null)
{
editor.Restore(memento);
}
}
}
Output:
Content set to: Hello
Saving state...
Content set to: Hello World
Saving state...
Content set to: Hello World!
Saving state...
Undoing...
Restored to: Hello World
Undoing again...
Restored to: Hello
Real-World Example: Game Checkpoint System
// Memento
public class GameMemento
{
public int Level { get; private set; }
public int Score { get; private set; }
public int Health { get; private set; }
public GameMemento(int level, int score, int health)
{
Level = level;
Score = score;
Health = health;
}
}
// Originator
public class Game
{
private int _level = 1;
private int _score = 0;
private int _health = 100;
public void Play()
{
_level++;
_score += 100;
_health -= 10;
Console.WriteLine($"Level: {_level}, Score: {_score}, Health: {_health}");
}
public GameMemento SaveCheckpoint()
{
Console.WriteLine("Checkpoint saved");
return new GameMemento(_level, _score, _health);
}
public void LoadCheckpoint(GameMemento memento)
{
_level = memento.Level;
_score = memento.Score;
_health = memento.Health;
Console.WriteLine($"Checkpoint loaded - Level: {_level}, Score: {_score}, Health: {_health}");
}
public void ShowState()
{
Console.WriteLine($"Current - Level: {_level}, Score: {_score}, Health: {_health}");
}
}
// Caretaker
public class CheckpointManager
{
private Stack<GameMemento> _checkpoints = new Stack<GameMemento>();
public void SaveCheckpoint(GameMemento memento)
{
_checkpoints.Push(memento);
}
public GameMemento LoadLastCheckpoint()
{
if (_checkpoints.Count > 0)
{
return _checkpoints.Pop();
}
return null;
}
}
// Usage
var game = new Game();
var checkpointManager = new CheckpointManager();
game.Play();
checkpointManager.SaveCheckpoint(game.SaveCheckpoint());
game.Play();
game.Play();
game.ShowState();
Console.WriteLine("\nLoading checkpoint...");
var checkpoint = checkpointManager.LoadLastCheckpoint();
if (checkpoint != null)
{
game.LoadCheckpoint(checkpoint);
}
Configuration Snapshot Example
// Memento
public class ConfigMemento
{
public Dictionary<string, string> Settings { get; private set; }
public ConfigMemento(Dictionary<string, string> settings)
{
Settings = new Dictionary<string, string>(settings);
}
}
// Originator
public class Configuration
{
private Dictionary<string, string> _settings = new Dictionary<string, string>();
public void SetSetting(string key, string value)
{
_settings[key] = value;
Console.WriteLine($"Setting {key} = {value}");
}
public string GetSetting(string key)
{
return _settings.ContainsKey(key) ? _settings[key] : null;
}
public ConfigMemento CreateSnapshot()
{
return new ConfigMemento(_settings);
}
public void RestoreSnapshot(ConfigMemento memento)
{
_settings = new Dictionary<string, string>(memento.Settings);
Console.WriteLine("Configuration restored from snapshot");
}
public void ShowSettings()
{
foreach (var setting in _settings)
{
Console.WriteLine($"{setting.Key} = {setting.Value}");
}
}
}
// Usage
var config = new Configuration();
config.SetSetting("Theme", "Dark");
config.SetSetting("Language", "English");
var snapshot = config.CreateSnapshot();
config.SetSetting("Theme", "Light");
config.SetSetting("FontSize", "14");
config.ShowSettings();
Console.WriteLine("\nRestoring snapshot...");
config.RestoreSnapshot(snapshot);
config.ShowSettings();
Applications
Enterprise Use Cases:
- Undo/Redo Functionality: Text editors, graphics applications, form editors
- Checkpoint Systems: Games, simulation systems
- Configuration Management: Saving and restoring system configurations
- Transaction Rollback: Database transactions, financial systems
- State Snapshots: Saving application state for recovery
- Version Control: Saving versions of documents or data
- Backup Systems: Creating restore points
- Workflow State: Saving workflow state for resumption
Benefits:
- Preserves encapsulation boundaries
- Simplifies originator by not storing backup state internally
- Allows state restoration without exposing implementation
- Enables undo/redo functionality
- Supports checkpoint and rollback operations
When to Use:
- You need to save and restore object state
- Direct access to state would break encapsulation
- You need undo/redo functionality
- You need checkpoint/rollback capabilities
- You want to save state without exposing implementation details
Implementation Considerations:
- Mementos should be immutable
- Caretaker should not modify memento state
- Consider memory usage for large state or many mementos
- May need to implement memento serialization for persistence
Key Insight: The Memento pattern allows you to save and restore object state without breaking encapsulation. The originator creates mementos containing its state, and the caretaker manages these mementos. This enables undo/redo, checkpoints, and state restoration while keeping implementation details private.
