The Memento Pattern is a behavioral design pattern that allows capturing and externalizing an object's internal state without violating encapsulation, so the object can be restored to this state later.
1. Basic Memento Pattern Implementation
Core Structure
import java.util.ArrayList; import java.util.List; // Memento - stores the state of the Originator class TextMemento { private final String state; private final long timestamp; public TextMemento(String state) { this.state = state; this.timestamp = System.currentTimeMillis(); } public String getState() { return state; } public long getTimestamp() { return timestamp; } @Override public String toString() { return String.format("Memento[time=%tT, chars=%d]", timestamp, state.length()); } } // Originator - creates and uses mementos class TextEditor { private String content; private String font; private int fontSize; private String color; public TextEditor() { this.content = ""; this.font = "Arial"; this.fontSize = 12; this.color = "Black"; } // Write text public void write(String text) { this.content += text; System.out.println("Content: " + content); } // Delete last character public void delete() { if (!content.isEmpty()) { content = content.substring(0, content.length() - 1); System.out.println("Content after delete: " + content); } } // Formatting methods public void setFont(String font) { this.font = font; System.out.println("Font set to: " + font); } public void setFontSize(int fontSize) { this.fontSize = fontSize; System.out.println("Font size set to: " + fontSize); } public void setColor(String color) { this.color = color; System.out.println("Color set to: " + color); } // Create memento public TextMemento save() { System.out.println("Saving state..."); return new TextMemento(content); } // Restore from memento public void restore(TextMemento memento) { this.content = memento.getState(); System.out.println("Restored state from: " + memento); System.out.println("Current content: " + content); } // Get current state public void printState() { System.out.printf("Current State: '%s' [Font: %s, Size: %d, Color: %s]%n", content, font, fontSize, color); } public String getContent() { return content; } } // Caretaker - manages mementos class History { private final List<TextMemento> mementos = new ArrayList<>(); private int currentState = -1; public void saveState(TextMemento memento) { // Remove any future states if we're not at the end if (currentState < mementos.size() - 1) { mementos.subList(currentState + 1, mementos.size()).clear(); } mementos.add(memento); currentState = mementos.size() - 1; System.out.println("State saved. Total states: " + mementos.size()); } public TextMemento undo() { if (currentState <= 0) { System.out.println("Cannot undo - no previous state"); return null; } currentState--; TextMemento memento = mementos.get(currentState); System.out.println("Undo to: " + memento); return memento; } public TextMemento redo() { if (currentState >= mementos.size() - 1) { System.out.println("Cannot redo - no future state"); return null; } currentState++; TextMemento memento = mementos.get(currentState); System.out.println("Redo to: " + memento); return memento; } public void showHistory() { System.out.println("\n=== History ==="); for (int i = 0; i < mementos.size(); i++) { String marker = (i == currentState) ? "-> " : " "; System.out.println(marker + mementos.get(i)); } System.out.println(); } public void clear() { mementos.clear(); currentState = -1; System.out.println("History cleared"); } } // Client code public class MementoPatternDemo { public static void main(String[] args) { TextEditor editor = new TextEditor(); History history = new History(); // Edit text with saves editor.write("Hello"); history.saveState(editor.save()); editor.write(" World"); history.saveState(editor.save()); editor.write("!"); history.saveState(editor.save()); editor.printState(); history.showHistory(); // Undo operations System.out.println("\n=== Undo Operations ==="); editor.restore(history.undo()); editor.restore(history.undo()); history.showHistory(); // Redo operation System.out.println("=== Redo Operation ==="); editor.restore(history.redo()); // Continue editing System.out.println("\n=== Continue Editing ==="); editor.write(" Java"); history.saveState(editor.save()); editor.write(" Programming"); history.saveState(editor.save()); history.showHistory(); editor.printState(); } } 2. Real-World Example: Game Save System
Game State Management with Memento
import java.io.*; import java.util.*; // Game State Memento class GameSave implements Serializable { private final String playerName; private final int level; private final int score; private final int health; private final List<String> inventory; private final Map<String, Object> gameState; private final long timestamp; private final String saveName; public GameSave(String playerName, int level, int score, int health, List<String> inventory, Map<String, Object> gameState, String saveName) { this.playerName = playerName; this.level = level; this.score = score; this.health = health; this.inventory = new ArrayList<>(inventory); this.gameState = new HashMap<>(gameState); this.timestamp = System.currentTimeMillis(); this.saveName = saveName; } // Getters public String getPlayerName() { return playerName; } public int getLevel() { return level; } public int getScore() { return score; } public int getHealth() { return health; } public List<String> getInventory() { return new ArrayList<>(inventory); } public Map<String, Object> getGameState() { return new HashMap<>(gameState); } public long getTimestamp() { return timestamp; } public String getSaveName() { return saveName; } @Override public String toString() { return String.format("Save: %s [Player: %s, Level: %d, Score: %d, Health: %d, Items: %d]", saveName, playerName, level, score, health, inventory.size()); } } // Originator - Game Character class GameCharacter { private String playerName; private int level; private int score; private int health; private List<String> inventory; private Map<String, Object> gameState; public GameCharacter(String playerName) { this.playerName = playerName; this.level = 1; this.score = 0; this.health = 100; this.inventory = new ArrayList<>(); this.gameState = new HashMap<>(); // Initial items inventory.add("Sword"); inventory.add("Potion"); } // Game actions public void levelUp() { level++; score += 100; health = 100; // Full health on level up System.out.println(playerName + " leveled up to level " + level + "!"); } public void takeDamage(int damage) { health = Math.max(0, health - damage); System.out.println(playerName + " took " + damage + " damage. Health: " + health); if (health == 0) { System.out.println(playerName + " has been defeated!"); } } public void heal(int amount) { health = Math.min(100, health + amount); System.out.println(playerName + " healed " + amount + " points. Health: " + health); } public void addScore(int points) { score += points; System.out.println("+" + points + " points! Total score: " + score); } public void addItem(String item) { inventory.add(item); System.out.println("Added item: " + item); } public void useItem(String item) { if (inventory.remove(item)) { System.out.println("Used item: " + item); // Item effects switch (item.toLowerCase()) { case "potion": heal(30); break; case "super potion": heal(60); break; case "mega potion": health = 100; System.out.println("Fully healed!"); break; } } else { System.out.println("Item not found: " + item); } } public void setGameState(String key, Object value) { gameState.put(key, value); } public Object getGameState(String key) { return gameState.get(key); } // Create memento public GameSave saveGame(String saveName) { System.out.println("Saving game: " + saveName); return new GameSave(playerName, level, score, health, inventory, gameState, saveName); } // Restore from memento public void loadGame(GameSave save) { this.playerName = save.getPlayerName(); this.level = save.getLevel(); this.score = save.getScore(); this.health = save.getHealth(); this.inventory = save.getInventory(); this.gameState = save.getGameState(); System.out.println("Game loaded: " + save.getSaveName()); printStatus(); } public void printStatus() { System.out.printf("\n=== %s Status ===%n", playerName); System.out.printf("Level: %d%n", level); System.out.printf("Score: %d%n", score); System.out.printf("Health: %d/100%n", health); System.out.printf("Inventory: %s%n", inventory); System.out.printf("Game State: %s%n%n", gameState); } } // Caretaker - Save Game Manager class SaveGameManager { private final Map<String, GameSave> saves = new LinkedHashMap<>(); private final String saveDirectory; public SaveGameManager(String saveDirectory) { this.saveDirectory = saveDirectory; createSaveDirectory(); loadSavesFromDisk(); } private void createSaveDirectory() { File dir = new File(saveDirectory); if (!dir.exists()) { dir.mkdirs(); } } public void saveGame(GameSave save) { saves.put(save.getSaveName(), save); saveToDisk(save); System.out.println("Game saved: " + save.getSaveName()); } public GameSave loadGame(String saveName) { GameSave save = saves.get(saveName); if (save == null) { System.out.println("Save not found: " + saveName); return null; } return save; } public void deleteSave(String saveName) { GameSave removed = saves.remove(saveName); if (removed != null) { deleteFromDisk(saveName); System.out.println("Save deleted: " + saveName); } else { System.out.println("Save not found: " + saveName); } } public List<String> listSaves() { return new ArrayList<>(saves.keySet()); } public void showSaves() { System.out.println("\n=== Available Saves ==="); if (saves.isEmpty()) { System.out.println("No saves available"); return; } saves.values().forEach(save -> { System.out.printf("- %s (Level: %d, Score: %d, Health: %d)%n", save.getSaveName(), save.getLevel(), save.getScore(), save.getHealth()); }); System.out.println(); } // Serialization methods private void saveToDisk(GameSave save) { try (ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream(saveDirectory + "/" + save.getSaveName() + ".sav"))) { oos.writeObject(save); } catch (IOException e) { System.err.println("Error saving game: " + e.getMessage()); } } private void loadSavesFromDisk() { File dir = new File(saveDirectory); File[] saveFiles = dir.listFiles((d, name) -> name.endsWith(".sav")); if (saveFiles != null) { for (File file : saveFiles) { try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) { GameSave save = (GameSave) ois.readObject(); String saveName = file.getName().replace(".sav", ""); saves.put(saveName, save); } catch (IOException | ClassNotFoundException e) { System.err.println("Error loading save: " + e.getMessage()); } } } } private void deleteFromDisk(String saveName) { File file = new File(saveDirectory + "/" + saveName + ".sav"); if (file.exists()) { file.delete(); } } } // Client code public class GameSaveSystemDemo { public static void main(String[] args) { // Create game character GameCharacter player = new GameCharacter("Hero"); SaveGameManager saveManager = new SaveGameManager("game_saves"); // Initial state player.printStatus(); // Play the game System.out.println("=== Gameplay ==="); player.addScore(50); player.levelUp(); player.addItem("Super Potion"); player.takeDamage(30); player.useItem("Potion"); // Save game saveManager.saveGame(player.saveGame("initial_progress")); player.printStatus(); // Continue playing System.out.println("=== More Gameplay ==="); player.addScore(200); player.levelUp(); player.takeDamage(60); player.addItem("Magic Sword"); // Save again saveManager.saveGame(player.saveGame("good_progress")); player.printStatus(); // Show available saves saveManager.showSaves(); // Simulate game crash and reload System.out.println("=== Game Crash - Reloading ==="); GameSave loadedSave = saveManager.loadGame("initial_progress"); if (loadedSave != null) { player.loadGame(loadedSave); } // Try different save System.out.println("=== Loading Different Save ==="); loadedSave = saveManager.loadGame("good_progress"); if (loadedSave != null) { player.loadGame(loadedSave); } // Create multiple saves System.out.println("=== Multiple Saves ==="); player.addItem("Golden Key"); saveManager.saveGame(player.saveGame("with_key")); player.levelUp(); player.addItem("Dragon Scale"); saveManager.saveGame(player.saveGame("dragon_hunt")); saveManager.showSaves(); // Delete a save System.out.println("=== Deleting Save ==="); saveManager.deleteSave("initial_progress"); saveManager.showSaves(); } } 3. Document Editor with Undo/Redo
Advanced Memento for Document Editing
import java.util.ArrayDeque; import java.util.Deque; // Document Memento class DocumentMemento { private final String content; private final String selection; private final int cursorPosition; private final String font; private final int fontSize; private final long timestamp; private final String description; public DocumentMemento(String content, String selection, int cursorPosition, String font, int fontSize, String description) { this.content = content; this.selection = selection; this.cursorPosition = cursorPosition; this.font = font; this.fontSize = fontSize; this.timestamp = System.currentTimeMillis(); this.description = description; } // Getters public String getContent() { return content; } public String getSelection() { return selection; } public int getCursorPosition() { return cursorPosition; } public String getFont() { return font; } public int getFontSize() { return fontSize; } public long getTimestamp() { return timestamp; } public String getDescription() { return description; } @Override public String toString() { return String.format("DocumentState[%s: '%s...']", description, content.substring(0, Math.min(20, content.length()))); } } // Originator - Document class Document { private String content; private String selection; private int cursorPosition; private String font; private int fontSize; public Document() { this.content = ""; this.selection = ""; this.cursorPosition = 0; this.font = "Arial"; this.fontSize = 12; } // Editing operations public void insertText(String text) { String before = content.substring(0, cursorPosition); String after = content.substring(cursorPosition); content = before + text + after; cursorPosition += text.length(); selection = ""; System.out.println("Inserted: '" + text + "'"); } public void deleteText(int length) { if (cursorPosition >= length) { String before = content.substring(0, cursorPosition - length); String after = content.substring(cursorPosition); content = before + after; cursorPosition -= length; System.out.println("Deleted " + length + " characters"); } } public void selectText(int start, int end) { if (start >= 0 && end <= content.length() && start < end) { selection = content.substring(start, end); cursorPosition = end; System.out.println("Selected: '" + selection + "'"); } } public void boldSelection() { if (!selection.isEmpty()) { content = content.replace(selection, "**" + selection + "**"); selection = ""; System.out.println("Applied bold to selection"); } } public void italicSelection() { if (!selection.isEmpty()) { content = content.replace(selection, "_" + selection + "_"); selection = ""; System.out.println("Applied italic to selection"); } } public void setFont(String font) { this.font = font; System.out.println("Font changed to: " + font); } public void setFontSize(int fontSize) { this.fontSize = fontSize; System.out.println("Font size changed to: " + fontSize); } public void setCursorPosition(int position) { if (position >= 0 && position <= content.length()) { this.cursorPosition = position; System.out.println("Cursor moved to position: " + position); } } // Create memento with description public DocumentMemento saveState(String description) { return new DocumentMemento(content, selection, cursorPosition, font, fontSize, description); } // Restore from memento public void restoreState(DocumentMemento memento) { this.content = memento.getContent(); this.selection = memento.getSelection(); this.cursorPosition = memento.getCursorPosition(); this.font = memento.getFont(); this.fontSize = memento.getFontSize(); System.out.println("Restored: " + memento.getDescription()); } public void printState() { System.out.printf("\n=== Document State ===%n"); System.out.printf("Content: '%s'%n", content); System.out.printf("Selection: '%s'%n", selection); System.out.printf("Cursor: %d%n", cursorPosition); System.out.printf("Font: %s, Size: %d%n", font, fontSize); // Show cursor position visually if (!content.isEmpty()) { String visual = content.substring(0, cursorPosition) + "|" + content.substring(cursorPosition); System.out.printf("Visual: '%s'%n%n", visual); } else { System.out.printf("Visual: '|'%n%n"); } } } // Caretaker - Undo/Redo Manager class UndoRedoManager { private final Deque<DocumentMemento> undoStack = new ArrayDeque<>(); private final Deque<DocumentMemento> redoStack = new ArrayDeque<>(); private final int maxHistorySize; public UndoRedoManager(int maxHistorySize) { this.maxHistorySize = maxHistorySize; } public void saveState(DocumentMemento memento) { undoStack.push(memento); // Clear redo stack when new action is performed redoStack.clear(); // Limit history size if (undoStack.size() > maxHistorySize) { // Remove oldest state (from the bottom of the stack) ArrayDeque<DocumentMemento> temp = new ArrayDeque<>(); while (undoStack.size() > maxHistorySize - 1) { temp.push(undoStack.pop()); } undoStack.clear(); while (!temp.isEmpty()) { undoStack.push(temp.pop()); } } System.out.println("State saved: " + memento.getDescription()); } public DocumentMemento undo() { if (undoStack.size() <= 1) { System.out.println("Nothing to undo"); return null; } // Current state moves to redo stack DocumentMemento current = undoStack.pop(); redoStack.push(current); // Previous state becomes current DocumentMemento previous = undoStack.peek(); System.out.println("Undo: " + previous.getDescription()); return previous; } public DocumentMemento redo() { if (redoStack.isEmpty()) { System.out.println("Nothing to redo"); return null; } DocumentMemento next = redoStack.pop(); undoStack.push(next); System.out.println("Redo: " + next.getDescription()); return next; } public void showHistory() { System.out.println("\n=== Undo/Redo History ==="); System.out.println("Undo Stack (" + undoStack.size() + " states):"); undoStack.forEach(state -> System.out.println(" " + state)); System.out.println("Redo Stack (" + redoStack.size() + " states):"); redoStack.forEach(state -> System.out.println(" " + state)); System.out.println(); } public void clear() { undoStack.clear(); redoStack.clear(); System.out.println("History cleared"); } public boolean canUndo() { return undoStack.size() > 1; } public boolean canRedo() { return !redoStack.isEmpty(); } } // Client code public class DocumentEditorDemo { public static void main(String[] args) { Document document = new Document(); UndoRedoManager history = new UndoRedoManager(10); // Initial state history.saveState(document.saveState("Initial empty document")); document.printState(); // Edit document with saves System.out.println("=== Editing Document ==="); document.insertText("Hello World"); history.saveState(document.saveState("Added 'Hello World'")); document.setCursorPosition(6); document.insertText("Beautiful "); history.saveState(document.saveState("Inserted 'Beautiful '")); document.selectText(0, 5); document.boldSelection(); history.saveState(document.saveState("Applied bold to 'Hello'")); document.setFontSize(16); history.saveState(document.saveState("Increased font size")); document.printState(); history.showHistory(); // Undo/Redo operations System.out.println("=== Undo/Redo Operations ==="); document.restoreState(history.undo()); document.printState(); document.restoreState(history.undo()); document.printState(); document.restoreState(history.redo()); document.printState(); // Continue editing after undo/redo System.out.println("=== Continue Editing ==="); document.insertText("! This is a demo."); history.saveState(document.saveState("Added more text")); document.setFont("Times New Roman"); history.saveState(document.saveState("Changed font")); document.selectText(25, 29); document.italicSelection(); history.saveState(document.saveState("Applied italic to 'demo'")); document.printState(); history.showHistory(); // Multiple undos System.out.println("=== Multiple Undos ==="); while (history.canUndo()) { document.restoreState(history.undo()); } document.printState(); // Multiple redos System.out.println("=== Multiple Redos ==="); while (history.canRedo()) { document.restoreState(history.redo()); } document.printState(); } } 4. Configuration Management System
Memento for Configuration State Management
import java.util.*; import java.util.prefs.Preferences; // Configuration Memento class ConfigMemento { private final Map<String, Object> settings; private final String version; private final String description; private final long timestamp; public ConfigMemento(Map<String, Object> settings, String version, String description) { this.settings = new HashMap<>(settings); this.version = version; this.description = description; this.timestamp = System.currentTimeMillis(); } public Map<String, Object> getSettings() { return new HashMap<>(settings); } public String getVersion() { return version; } public String getDescription() { return description; } public long getTimestamp() { return timestamp; } @Override public String toString() { return String.format("Config[%s: %s, %d settings]", version, description, settings.size()); } } // Originator - Application Configuration class AppConfiguration { private Map<String, Object> settings; private String currentVersion; public AppConfiguration() { this.settings = new HashMap<>(); this.currentVersion = "1.0.0"; loadDefaultSettings(); } private void loadDefaultSettings() { settings.put("theme", "light"); settings.put("language", "en"); settings.put("autoSave", true); settings.put("autoSaveInterval", 5); settings.put("maxFileSize", 10485760); settings.put("recentFiles", new ArrayList<String>()); settings.put("windowWidth", 1200); settings.put("windowHeight", 800); settings.put("showLineNumbers", true); settings.put("fontSize", 14); } // Configuration operations public void setSetting(String key, Object value) { settings.put(key, value); System.out.printf("Setting updated: %s = %s%n", key, value); } public Object getSetting(String key) { return settings.get(key); } public void removeSetting(String key) { settings.remove(key); System.out.println("Setting removed: " + key); } public void setVersion(String version) { this.currentVersion = version; System.out.println("Version set to: " + version); } // Create memento public ConfigMemento saveConfig(String description) { return new ConfigMemento(settings, currentVersion, description); } // Restore from memento public void restoreConfig(ConfigMemento memento) { this.settings = memento.getSettings(); this.currentVersion = memento.getVersion(); System.out.println("Configuration restored: " + memento.getDescription()); } // Export to properties format public String exportToProperties() { StringBuilder sb = new StringBuilder(); sb.append("# Application Configuration\n"); sb.append("# Version: ").append(currentVersion).append("\n"); sb.append("# Export Time: ").append(new Date()).append("\n\n"); settings.forEach((key, value) -> { sb.append(key).append("=").append(value).append("\n"); }); return sb.toString(); } // Import from properties (simplified) public void importFromProperties(String properties) { // Simple import implementation String[] lines = properties.split("\n"); for (String line : lines) { if (!line.trim().isEmpty() && !line.startsWith("#")) { String[] parts = line.split("=", 2); if (parts.length == 2) { settings.put(parts[0].trim(), parseValue(parts[1].trim())); } } } System.out.println("Configuration imported from properties"); } private Object parseValue(String value) { // Simple value parsing if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) { return Boolean.parseBoolean(value); } try { return Integer.parseInt(value); } catch (NumberFormatException e) { // Try double try { return Double.parseDouble(value); } catch (NumberFormatException e2) { return value; } } } public void printConfig() { System.out.printf("\n=== Configuration (v%s) ===%n", currentVersion); settings.entrySet().stream() .sorted(Map.Entry.comparingByKey()) .forEach(entry -> System.out.printf(" %-20s = %s%n", entry.getKey(), entry.getValue())); System.out.println(); } } // Caretaker - Configuration Manager class ConfigManager { private final List<ConfigMemento> history = new ArrayList<>(); private final Map<String, ConfigMemento> namedSnapshots = new HashMap<>(); private final int maxHistorySize; public ConfigManager(int maxHistorySize) { this.maxHistorySize = maxHistorySize; } public void saveState(ConfigMemento memento) { history.add(memento); // Limit history size if (history.size() > maxHistorySize) { history.remove(0); // Remove oldest } System.out.println("Configuration state saved: " + memento.getDescription()); } public void saveNamedSnapshot(String name, ConfigMemento memento) { namedSnapshots.put(name, memento); System.out.println("Named snapshot saved: " + name); } public ConfigMemento undo() { if (history.size() <= 1) { System.out.println("Nothing to undo"); return null; } history.remove(history.size() - 1); // Remove current ConfigMemento previous = history.get(history.size() - 1); System.out.println("Undo to: " + previous.getDescription()); return previous; } public ConfigMemento getNamedSnapshot(String name) { ConfigMemento snapshot = namedSnapshots.get(name); if (snapshot == null) { System.out.println("Snapshot not found: " + name); } return snapshot; } public void deleteNamedSnapshot(String name) { if (namedSnapshots.remove(name) != null) { System.out.println("Snapshot deleted: " + name); } else { System.out.println("Snapshot not found: " + name); } } public void showHistory() { System.out.println("\n=== Configuration History ==="); for (int i = 0; i < history.size(); i++) { String marker = (i == history.size() - 1) ? "-> " : " "; System.out.println(marker + history.get(i)); } } public void showSnapshots() { System.out.println("\n=== Named Snapshots ==="); if (namedSnapshots.isEmpty()) { System.out.println("No named snapshots"); return; } namedSnapshots.forEach((name, snapshot) -> { System.out.printf(" %-15s : %s%n", name, snapshot.getDescription()); }); } public void rollbackToVersion(String version) { Optional<ConfigMemento> target = history.stream() .filter(m -> m.getVersion().equals(version)) .reduce((first, second) -> second); // Get last occurrence if (target.isPresent()) { System.out.println("Rolling back to version: " + version); // In real implementation, you'd return this for restoration } else { System.out.println("Version not found in history: " + version); } } } // Client code public class ConfigManagementDemo { public static void main(String[] args) { AppConfiguration config = new AppConfiguration(); ConfigManager configManager = new ConfigManager(5); // Initial state configManager.saveState(config.saveConfig("Initial default configuration")); config.printConfig(); // Modify configuration System.out.println("=== Modifying Configuration ==="); config.setSetting("theme", "dark"); config.setSetting("fontSize", 16); config.setSetting("autoSaveInterval", 10); configManager.saveState(config.saveConfig("Dark theme with larger font")); config.setVersion("1.1.0"); config.setSetting("language", "fr"); config.setSetting("showLineNumbers", false); configManager.saveState(config.saveConfig("French language setup")); // Save named snapshots configManager.saveNamedSnapshot("work-setup", config.saveConfig("Work configuration snapshot")); config.setSetting("theme", "blue"); config.setSetting("fontSize", 18); configManager.saveNamedSnapshot("presentation-mode", config.saveConfig("Presentation mode configuration")); config.printConfig(); configManager.showHistory(); configManager.showSnapshots(); // Export configuration System.out.println("=== Exporting Configuration ==="); String exported = config.exportToProperties(); System.out.println(exported); // Undo changes System.out.println("=== Undo Operations ==="); config.restoreConfig(configManager.undo()); config.printConfig(); // Restore from named snapshot System.out.println("=== Restoring from Snapshot ==="); ConfigMemento workSnapshot = configManager.getNamedSnapshot("work-setup"); if (workSnapshot != null) { config.restoreConfig(workSnapshot); config.printConfig(); } // Show final state configManager.showHistory(); configManager.showSnapshots(); } } Key Benefits of Memento Pattern
- Encapsulation Preservation: Doesn't expose object's internal state
- State Management: Easy undo/redo functionality
- Snapshot Capability: Can capture object state at any point
- Simplified Originator: Originator doesn't need to manage state storage
- Flexible Restoration: Can restore to any previous state
When to Use Memento Pattern
- When you need to implement undo/redo functionality
- When you need to save and restore object state
- When direct access to object's state would break encapsulation
- When you need to implement snapshots for checkpoints
- When state restoration should be independent of the object's lifecycle
This comprehensive guide demonstrates how the Memento Pattern provides robust state management capabilities while maintaining encapsulation and separation of concerns.