From c8306a0794c470c282dd0c52e8cc06d68781dbc8 Mon Sep 17 00:00:00 2001 From: Michael Lizzio Date: Sun, 9 Nov 2025 14:57:49 -0500 Subject: Basic undo Mememto Pattern --- src/main/java/design/model/undo/Memento.java | 4 ++ src/main/java/design/model/undo/Originator.java | 7 ++ src/main/java/design/model/undo/UndoManager.java | 91 ++++++++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 src/main/java/design/model/undo/Memento.java create mode 100644 src/main/java/design/model/undo/Originator.java create mode 100644 src/main/java/design/model/undo/UndoManager.java (limited to 'src/main/java') diff --git a/src/main/java/design/model/undo/Memento.java b/src/main/java/design/model/undo/Memento.java new file mode 100644 index 0000000..162c3f3 --- /dev/null +++ b/src/main/java/design/model/undo/Memento.java @@ -0,0 +1,4 @@ +package design.model.undo; + +public interface Memento { +} diff --git a/src/main/java/design/model/undo/Originator.java b/src/main/java/design/model/undo/Originator.java new file mode 100644 index 0000000..63eac38 --- /dev/null +++ b/src/main/java/design/model/undo/Originator.java @@ -0,0 +1,7 @@ +package design.model.undo; + +public interface Originator { + Memento createMemento(); + + void restore(Memento memento); +} \ No newline at end of file diff --git a/src/main/java/design/model/undo/UndoManager.java b/src/main/java/design/model/undo/UndoManager.java new file mode 100644 index 0000000..5c6a4a0 --- /dev/null +++ b/src/main/java/design/model/undo/UndoManager.java @@ -0,0 +1,91 @@ +package design.model.undo; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.List; +import java.util.stream.Collectors; + +// Singleton caretaker that handles undo and redo stacks. +public final class UndoManager { + + private static final UndoManager INSTANCE = new UndoManager(); + + public static UndoManager instance() { + return INSTANCE; + } + + private UndoManager() { + } + + // Stack entry + private static class Entry { + final Originator originator; + final Memento memento; + final String label; + + Entry(Originator originator, Memento memento, String label) { + this.originator = originator; + this.memento = memento; + this.label = label; + } + } + + private final Deque undoStack = new ArrayDeque<>(); + private final Deque redoStack = new ArrayDeque<>(); + + // Capture state + public void capture(Originator originator, String label) { + undoStack.push(new Entry(originator, originator.createMemento(), label)); + redoStack.clear(); + } + + // Undo / Redo ops + public boolean canUndo() { + return !undoStack.isEmpty(); + } + + public boolean canRedo() { + return !redoStack.isEmpty(); + } + + public String peekUndoLabel() { + return canUndo() ? undoStack.peek().label : null; + } + + public String peekRedoLabel() { + return canRedo() ? redoStack.peek().label : null; + } + + public List getUndoHistoryLabels() { + return undoStack.stream() + .map(e -> e.label) + .collect(Collectors.toList()); + } + + public void undo() { + if (!canUndo()) { + System.out.println("Nothing to undo."); + return; + } + + Entry entry = undoStack.pop(); + redoStack.push(new Entry(entry.originator, entry.originator.createMemento(), entry.label)); + entry.originator.restore(entry.memento); + } + + public void redo() { + if (!canRedo()) { + System.out.println("Nothing to redo."); + return; + } + + Entry entry = redoStack.pop(); + undoStack.push(new Entry(entry.originator, entry.originator.createMemento(), entry.label)); + entry.originator.restore(entry.memento); + } + + public void purge() { + undoStack.clear(); + redoStack.clear(); + } +} -- cgit v1.2.3