Hubbry Logo
Memento patternMemento patternMain
Open search
Memento pattern
Community hub
Memento pattern
logo
7 pages, 0 posts
0 subscribers
Be the first to start a discussion here.
Be the first to start a discussion here.
Memento pattern
Memento pattern
from Wikipedia

The memento pattern is a software design pattern that exposes the private internal state of an object. One example of how this can be used is to restore an object to its previous state (undo via rollback), another is versioning, another is custom serialization.

The memento pattern is implemented with three objects: the originator, a caretaker and a memento. The originator is some object that has an internal state. The caretaker is going to do something to the originator, but wants to be able to undo the change. The caretaker first asks the originator for a memento object. Then it does whatever operation (or sequence of operations) it was going to do. To roll back to the state before the operations, it returns the memento object to the originator. The memento object itself is an opaque object (one which the caretaker cannot, or should not, change). When using this pattern, care should be taken if the originator may change other objects or resources—the memento pattern operates on a single object.

Classic examples of the memento pattern include a pseudorandom number generator (each consumer of the PRNG serves as a caretaker who can initialize the PRNG (the originator) with the same seed (the memento) to produce an identical sequence of pseudorandom numbers) and the state in a finite state machine.

Structure

[edit]

UML class and sequence diagram

[edit]
A sample UML class and sequence diagram for the Memento design pattern.[1]

In the above UML class diagram, the Caretaker class refers to the Originator class for saving (createMemento()) and restoring (restore(memento)) originator's internal state.
The Originator class implements
(1) createMemento() by creating and returning a Memento object that stores originator's current internal state and
(2) restore(memento) by restoring state from the passed in Memento object.

The UML sequence diagram shows the run-time interactions:
(1) Saving originator's internal state: The Caretaker object calls createMemento() on the Originator object, which creates a Memento object, saves its current internal state (setState()), and returns the Memento to the Caretaker.
(2) Restoring originator's internal state: The Caretaker calls restore(memento) on the Originator object and specifies the Memento object that stores the state that should be restored. The Originator gets the state (getState()) from the Memento to set its own state.

Java example

[edit]

The following Java program illustrates the "undo" usage of the memento pattern.

package org.wikipedia.examples;

import java.util.ArrayList;
import java.util.List;

class Originator {
    private String state;
    // The class could also contain additional data that is not part of the
    // state saved in the memento..
 
    public void set(String state) {
        this.state = state;
        System.out.printf("Originator: Setting state to %s%n", state);
    }
 
    public Memento saveToMemento() {
        System.out.println("Originator: Saving to Memento.");
        return new Memento(this.state);
    }
 
    public void restoreFromMemento(Memento memento) {
        this.state = memento.getSavedState();
        System.out.printf("Originator: State after restoring from Memento: %s%n", state);
    }
 
    public static class Memento {
        private final String state;

        public Memento(String stateToSave) {
            state = stateToSave;
        }
 
        // accessible by outer class only
        private String getSavedState() {
            return state;
        }
    }
}
 
class Caretaker {
    public static void main(String[] args) {
        List<Originator.Memento> savedStates = new ArrayList<Originator.Memento>();
 
        Originator originator = new Originator();
        originator.set("State1");
        originator.set("State2");
        savedStates.add(originator.saveToMemento());
        originator.set("State3");
        // We can request multiple mementos, and choose which one to roll back to.
        savedStates.add(originator.saveToMemento());
        originator.set("State4");
 
        originator.restoreFromMemento(savedStates.get(1));   
    }
}

The output is:

Originator: Setting state to State1
Originator: Setting state to State2
Originator: Saving to Memento.
Originator: Setting state to State3
Originator: Saving to Memento.
Originator: Setting state to State4
Originator: State after restoring from Memento: State3

This example uses a String as the state, which is an immutable object in Java. In real-life scenarios the state will almost always be a mutable object, in which case a copy of the state must be made.

It must be said that the implementation shown has a drawback: it declares an internal class. It would be better if this memento strategy could apply to more than one originator.

There are mainly three other ways to achieve Memento:

  1. Serialization.
  2. A class declared in the same package.
  3. The object can also be accessed via a proxy, which can achieve any save/restore operation on the object.

C# example

[edit]

The memento pattern allows one to capture the internal state of an object without violating encapsulation such that later one can undo/revert the changes if required. Here one can see that the memento object is actually used to revert the changes made in the object.

namespace Wikipedia.Examples;

class Memento
{
    private readonly string _savedState;

    private Memento(string stateToSave)
    {
        _savedState = stateToSave;
    }

    public class Originator
    {
        private string _state;
        // The class could also contain additional data that is not part of the
        // state saved in the memento.

        public void Set(string state)
        {
            Console.WriteLine(f"Originator: Setting state to {state}");
            _state = state;
        }

        public Memento SaveToMemento()
        {
            Console.WriteLine("Originator: Saving to Memento.");
            return new Memento(_state);
        }

        public void RestoreFromMemento(Memento memento)
        {
            _state = memento.savedState;
            Console.WriteLine(f"Originator: State after restoring from Memento: {_state}");
        }
    }
}

class Caretaker
{
    static void Main(string[] args)
    {
        List<Memento> savedStates = new();

        Memento.Originator originator = new();
        originator.Set("State1");
        originator.Set("State2");
        savedStates.Add(originator.SaveToMemento());
        originator.Set("State3");
        // We can request multiple mementos, and choose which one to roll back to.
        savedStates.Add(originator.SaveToMemento());
        originator.Set("State4");

        originator.RestoreFromMemento(savedStates[1]);
    }
}

Python example

[edit]
"""
Memento pattern example.
"""

class Originator:
    state: str = ""

    def set(self, state: str) -> None:
        print(f"Originator: Setting state to {state}")
        self.state = state

    def save_to_memento(self) -> "Memento":
        return self.Memento(self.state)

    def restore_from_memento(self, memento: "Memento") -> None:
        self.state = memento.get_saved_state()
        print(f"Originator: State after restoring from Memento: {self.state}")

    class Memento:
        state: str

        def __init__(self, state: str) -> None:
            self.state = state

        def get_saved_state(self) -> str:
            return self.state

saved_states: list[Memento] = []
originator: Originator = Originator()

originator.set("State1")
originator.set("State2")
saved_states.append(originator.save_to_memento())

originator.set("State3")
saved_states.append(originator.save_to_memento())

originator.set("State4")

originator.restore_from_memento(saved_states[1])

JavaScript example

[edit]
// The Memento pattern is used to save and restore the state of an object.
// A memento is a snapshot of an object's state.
let Memento = {// Namespace: Memento
    savedState : null, // The saved state of the object.

    save : function(state) { // Save the state of an object.
        this.savedState = state;
    },

    restore : function() { // Restore the state of an object.
        return this.savedState;
    }
};

// The Originator is the object that creates the memento.
// defines a method for saving the state inside a memento.
let Originator = {// Namespace: Originator
        state : null, // The state to be stored

        // Creates a new originator with an initial state of null
        createMemento : function() { 
            return {
                state : this.state // The state is copied to the memento.
            };
        },
        setMemento : function(memento) { // Sets the state of the originator from a memento
            this.state = memento.state;
        }
    };


// The Caretaker stores mementos of the objects and
// provides operations to retrieve them.
let Caretaker = {// Namespace: Caretaker
        mementos : [], // The mementos of the objects.
        addMemento : function(memento) { // Add a memento to the collection.
            this.mementos.push(memento);
        },
        getMemento : function(index) { // Get a memento from the collection.
            return this.mementos[index];
        }
    };

let action_step = "Foo"; // The action to be executed/the object state to be stored.
let action_step_2 = "Bar"; // The action to be executed/the object state to be stored.

// set the initial state
Originator.state = action_step;
Caretaker.addMemento(Originator.createMemento());// save the state to the history
console.log("Initial State: " + Originator.state); // Foo

// change the state
Originator.state = action_step_2;
Caretaker.addMemento(Originator.createMemento()); // save the state to the history
console.log("State After Change: " + Originator.state); // Bar

// restore the first state - undo
Originator.setMemento(Caretaker.getMemento(0));
console.log("State After Undo: " + Originator.state); // Foo

// restore the second state - redo
Originator.setMemento(Caretaker.getMemento(1));
console.log("State After Redo: " + Originator.state); // Bar

References

[edit]
[edit]
Revisions and contributorsEdit on WikipediaRead on Wikipedia
from Grokipedia
The Memento pattern is a behavioral design pattern in that enables an object, known as the originator, to capture and externalize its internal state into a separate object called a memento, without violating encapsulation, thereby allowing the state to be restored later as needed. This pattern was introduced in 1994 as part of the seminal work Design Patterns: Elements of Reusable Object-Oriented Software by , Richard Helm, Ralph Johnson, and John Vlissides, commonly referred to as the (GoF) book, which cataloged 23 foundational design patterns for . At its core, the Memento pattern facilitates operations like undo and redo in applications by treating the memento's stored state as a snapshot that can be archived, transmitted, or discarded without exposing the originator's sensitive data to unauthorized access. The structure typically involves three key participants: the originator, which defines the state and creates or restores mementos; the memento, a value object that holds the state opaquely (with narrow and wide interfaces for controlled access); and the caretaker, which manages a collection of mementos—such as a stack for history—without modifying their contents. This design preserves encapsulation by ensuring that only the originator can interpret the memento's full state, while external entities see it as an immutable . The pattern's primary intent is to support state rollback and transaction-like behaviors in complex systems, making it particularly useful in user interfaces, text editors, and game development where reversible actions are essential. However, it can introduce trade-offs, such as increased memory consumption from storing multiple snapshots, especially for large objects, and potential performance overhead in serialization-heavy scenarios. Variants include nested memento classes for stricter encapsulation or interface-based implementations to enhance flexibility across languages like , C++, and Python. Overall, the Memento pattern exemplifies how behavioral patterns coordinate object interactions to achieve maintainable and extensible software architectures.

Overview

Definition and Intent

The Memento pattern is a behavioral that enables the capture and externalization of an object's internal state without violating encapsulation, allowing the object to be restored to that state later. This approach treats the saved state as an opaque token, preventing unauthorized access to sensitive internal details while supporting reliable . The primary intent of the Memento pattern is to facilitate operations like , redo, or versioning by externalizing an object's state in a controlled manner, ensuring that the restoration process does not compromise the object's integrity or reveal its implementation. By design, it addresses the challenge of preserving state snapshots for deferred use, such as in interactive applications where users need to revert changes without exposing the underlying . Encapsulation is maintained through the use of a dedicated memento object that acts as an : it stores the state in a form accessible only to the originating object, while external components, such as a caretaker, interact with it via a limited interface that hides the specifics of the stored data. This role ensures that state transfer remains secure and modular, aligning with object-oriented principles of .

Historical Context

The Memento pattern was formally introduced in the influential 1994 book Design Patterns: Elements of Reusable Object-Oriented Software by , Richard Helm, Ralph Johnson, and John Vlissides, collectively known as the (GoF). This work cataloged 23 reusable solutions to common problems in object-oriented software design, with the Memento classified among the behavioral patterns for its focus on object communication and responsibility assignment. The pattern's name derives from the Latin word memento, meaning "remember," symbolizing a token or keepsake that preserves an object's prior state, much like a memento in or theater serves as a reminder of past events or conditions. In the GoF framework, it addresses the need to externalize internal state snapshots without compromising encapsulation, drawing from practical requirements in interactive applications like graphical editors. Although formalized in 1994, precursors to the Memento pattern emerged in late-1980s discussions, particularly around in early interactive systems and languages like Smalltalk, where mechanisms for capturing and restoring object states supported incremental development and error recovery. The pattern saw adoption in 1990s GUI frameworks to enable operations, allowing applications to revert user actions by reloading saved states without exposing underlying object details. A key milestone was its integration into the GoF catalog, which standardized the pattern and spurred its use across ; this legacy aligns with state persistence features in modern languages, such as Java's Serializable interface (introduced in JDK 1.1 in 1997), which can be used to implement externalization of object states while supporting encapsulation goals similar to those of the Memento pattern.

Motivation and Applicability

Problem It Solves

The Memento pattern addresses the challenge of capturing and externalizing an object's internal state for later restoration without compromising encapsulation, a critical requirement in object-oriented design where direct access to private fields could lead to tight between components or unintended modifications. This problem arises in scenarios requiring state preservation, such as implementing mechanisms, where an application's core objects must maintain their while allowing external entities—like a or transaction manager—to store and retrieve snapshots of their state. Without a structured approach, developers risk exposing sensitive implementation details, potentially introducing vulnerabilities or making the system brittle to future changes. A classic example occurs in a text editor application, where users expect to undo typing or editing actions by reverting the document to its prior state, including not only the text content but also cursor position and formatting attributes. To achieve this, the editor (the originator object) must create a snapshot of its internal state at key moments, but providing public methods to access or set these private fields would violate encapsulation principles outlined in foundational design literature, allowing external code to inadvertently alter the object's consistency. This encapsulation breach could propagate errors across the system, as changes to the editor's internals might require updates to all dependent components. Beyond editors, the pattern tackles related issues in areas like for versioning object states during iterative computations, where partial exposure of internal data might compromise the simulation's accuracy or lead to failures. Similarly, in database systems supporting transaction rollbacks, preserving the pre-transaction state of complex entities without revealing proprietary structures is essential to prevent cascading failures or unauthorized access. These contexts highlight the need for a mechanism that isolates state storage from the object's core logic, ensuring that external caregivers can manage snapshots without deep knowledge of the internals. Alternative approaches, such as direct of objects or implementing ad-hoc getter and methods for all private fields, often fail because they either bypass encapsulation entirely—exposing the object's fragile internals to external modification—or increase maintenance overhead by requiring extensive refactoring whenever the internal structure evolves. For instance, public serialization might serialize unnecessary or sensitive data, raising concerns, while proliferating accessors creates a facade of simplicity that ties the object's evolution to its consumers, leading to higher and reduced reusability. The Memento pattern circumvents these pitfalls by designating a dedicated, opaque container for state, accessible only by the originator itself.

When to Use

The Memento pattern is particularly suitable for implementing and redo functionality in interactive applications, such as text editors or graphical user interfaces, where users need to revert to previous states without compromising the object's internal representation. It also supports transaction rollbacks in systems, allowing safe restoration of an object's state after a series of operations, as exemplified in command-based histories that require . This pattern finds ideal application in contexts where an object's state undergoes frequent modifications and requires restoration to a prior snapshot, such as in or managers that maintain trails. It ensures that external entities can capture and reinstate the state without gaining direct access to sensitive internal details, thereby preserving encapsulation while enabling features like state checkpointing in constraint solvers or iterative algorithms. To determine applicability, consider the following checklist derived from the pattern's core intent:
  • The object possesses a complex or voluminous internal state that cannot be easily exposed or copied via interfaces.
  • The caretaker (e.g., a controller or history manager) should not need to comprehend or manipulate the full state details, relying instead on opaque access.
  • A distinction between narrow (, limited) and wide (private, comprehensive) interfaces for the memento is feasible, allowing the originator full control over state while restricting external views.
While effective for these scenarios, the Memento pattern is not recommended for objects with very large states, as frequent snapshot creation can impose significant memory overhead, a concern explored further in discussions of its liabilities.

Components and Structure

Participants and Responsibilities

The Memento pattern involves three primary participants: the Originator, the Memento, and the Caretaker, each with distinct roles in capturing, storing, and restoring an object's internal state while preserving encapsulation. The Originator is the core object whose state requires preservation for potential restoration, such as in operations or state checkpoints. It is responsible for creating a Memento that encapsulates a snapshot of its current internal state by copying the state and for restoring its state from a provided Memento when needed, ensuring that the Memento accurately reflects and reinstates the Originator's data without exposing implementation details to external entities. The Memento serves as an opaque that stores the Originator's state in a protected manner, acting as a capsule that hides the details from unauthorized access. It provides a narrow interface to the Caretaker that offers no access to the internal state, while offering a wide interface exclusively to the Originator for full access to the stored data. To enhance safety, the Memento is often designed to be immutable once created, preventing unintended alterations during storage. The Caretaker manages the lifecycle of Mementos without ever directly accessing or altering the encapsulated state, thereby maintaining the Originator's privacy. It is tasked with deciding when to request a Memento from the Originator for saving—such as pushing it onto a stack for historical tracking—and when to pass a Memento back to the Originator for state restoration. This participant orchestrates the overall process, such as implementing /redo functionality, but remains oblivious to the specifics of the state to uphold encapsulation principles.

UML Diagrams

The UML class diagram for the Memento pattern illustrates the static structure involving three primary participants: the Originator, Memento, and Caretaker. The Originator class includes methods such as createMemento() to generate a snapshot of its internal state and setMemento(memento) to restore from a provided Memento; its internal state (e.g., a private field like state) is not directly accessible to external classes. The Memento class features a private state field encapsulating the Originator's data, along with getState() and setState(state) methods that form the wide interface accessible only to the Originator, while the narrow interface provides no methods for accessing the internal state, making it opaque to the Caretaker. The Caretaker class manages Mementos through methods like add(memento) and getMemento(), without access to the internal state. Relationships are depicted as associations: the Originator creates and uses the Memento (solid line with arrow for creation), and the Caretaker holds the Memento (composition or aggregation). Private visibility is denoted by minus signs (-) on the state field to emphasize encapsulation, while public methods use plus signs (+); no inheritance is involved, but dependencies may be shown as dotted lines if the Memento references the Originator for restoration. This class diagram highlights the narrow/wide interface dichotomy central to the pattern: the narrow interface restricts the Caretaker to non-sensitive operations, preventing unintended state exposure, whereas the wide interface allows the Originator full access to restore its exact prior state, preserving object-oriented encapsulation as defined in the original pattern description. The sequence diagram depicts the dynamic interactions for saving and restoring state. It begins with the Caretaker invoking createMemento() on the Originator, which internally constructs a new Memento object by copying its current state via the constructor or setter methods. The Originator then passes this Memento to the Caretaker for storage (e.g., via add(memento)). Later, to restore, the Caretaker retrieves a Memento (e.g., via getMemento()) and calls setMemento(memento) on the Originator, which uses the wide interface to update its private state from the Memento's encapsulated data. Lifelines represent the participants vertically, with activation bars indicating method execution; synchronous messages are solid arrows, and the flow emphasizes the Caretaker's role as an intermediary without state knowledge. A textual representation of the sequence diagram is as follows:

Participant: Caretaker Originator Memento | | | |---createMemento()-->| | | |---new()-->| |<--memento----| | |---add(memento)---------->| | | | |<--getMemento()-----------| |---setMemento(m)-->| | | |<--state--| | | |

Participant: Caretaker Originator Memento | | | |---createMemento()-->| | | |---new()-->| |<--memento----| | |---add(memento)---------->| | | | |<--getMemento()-----------| |---setMemento(m)-->| | | |<--state--| | | |

These diagrams collectively underscore the pattern's focus on undoable operations while maintaining the Originator's internal invariants.

Implementation

Key Considerations

When implementing the Memento pattern, encapsulation is a primary concern to prevent unauthorized access to the Originator's internal state. One effective strategy in object-oriented languages like is to define the Memento as a non-public inner class within the Originator, which allows the Originator full access to the Memento's fields while restricting the Caretaker to only essential metadata, such as a version identifier. This approach maintains strict privacy of the state without exposing it through public interfaces. Alternatively, an interface can be used for the Caretaker's view of the Memento, exposing only read-only properties, while the full Memento class remains package-private or otherwise limited. Handling immutable versus mutable states requires careful design to ensure the integrity of saved snapshots. Mementos should ideally be immutable, with state set only through constructors and no setter methods, to prevent accidental modifications after creation and to avoid side effects during restoration. For mutable states in complex Originator objects, such as those containing collections or nested objects, a shallow copy may suffice for simple primitives but often leads to pitfalls like unintended shared references; in these cases, implementing deep copies during Memento creation is necessary to fully isolate the historical state. Memory management poses significant trade-offs, particularly when storing multiple Mementos for features like undo histories. Accumulating a stack or list of Mementos in the Caretaker can consume substantial RAM, especially for large or frequently updated Originator states, potentially leading to performance degradation or out-of-memory errors in resource-constrained environments. To mitigate this, developers should monitor the Caretaker's lifecycle and explicitly discard obsolete Mementos to aid garbage collection, and for very large objects, consider optimizations like storing only deltas or using lazy loading to defer full state reconstruction until restoration. Thread safety must be addressed if Mementos are created, stored, or restored in concurrent scenarios, as unsynchronized access could corrupt the state or lead to inconsistent snapshots. Since Mementos are typically not shared across threads but rather passed between Originator and Caretaker, synchronization—such as using locks around create/restore operations or making Mementos thread-local—ensures atomicity without overcomplicating the pattern. In multi-threaded applications, avoiding concurrent modifications to the same Originator instance during Memento operations is crucial to prevent race conditions. The pattern supports variations like internal and external Memento configurations to balance encapsulation and flexibility. In the internal variation, the Memento is tightly coupled as a nested class, enhancing privacy but reducing reusability across different Originators. The external variation uses a separate, interface-driven Memento class, allowing broader applicability but requiring additional safeguards, such as friend-like access controls, to protect the state from external tampering. These choices depend on the application's needs for modularity versus security.

Pseudocode Example

The Memento pattern can be illustrated through abstract pseudocode that captures the essential interactions among the Originator, Memento, and Caretaker classes, emphasizing the encapsulation of state without exposing internal details.

Originator Pseudocode

The Originator maintains the current state and provides methods to create a Memento capturing that state, as well as to restore from a Memento using a wide interface that allows full access to the encapsulated data.

class Originator { private state; // Internal state to be saved/restored method setState(newState) { this.state = newState; } method getState() { return this.state; } // Creates a new Memento with a copy of the current state (narrow interface exposed to Caretaker) method createMemento() { return new Memento(this.getState()); } // Restores state from Memento using wide interface (full access to Memento's private state) method restoreFromMemento(memento) { this.state = memento.getFullState(); // Direct access to internal state copy } }

class Originator { private state; // Internal state to be saved/restored method setState(newState) { this.state = newState; } method getState() { return this.state; } // Creates a new Memento with a copy of the current state (narrow interface exposed to Caretaker) method createMemento() { return new Memento(this.getState()); } // Restores state from Memento using wide interface (full access to Memento's private state) method restoreFromMemento(memento) { this.state = memento.getFullState(); // Direct access to internal state copy } }

Memento Pseudocode

The Memento serves as an opaque value object that stores the Originator's state. It provides a narrow interface to the Caretaker (limited to basic storage/retrieval) and a wide interface to the Originator for restoration.

class Memento { private fullState; // Copy of Originator's state (immutable) constructor(stateCopy) { this.fullState = stateCopy; // Encapsulate the state } // Narrow interface: Caretaker can only retrieve a limited view (e.g., metadata), not full state method getStateMetadata() { return { timestamp: currentTime(), size: sizeof(this.fullState) }; // Example limited info } // Wide interface: Only Originator can access the full state for restoration method getFullState() { return this.fullState; // Full access granted to Originator } }

class Memento { private fullState; // Copy of Originator's state (immutable) constructor(stateCopy) { this.fullState = stateCopy; // Encapsulate the state } // Narrow interface: Caretaker can only retrieve a limited view (e.g., metadata), not full state method getStateMetadata() { return { timestamp: currentTime(), size: sizeof(this.fullState) }; // Example limited info } // Wide interface: Only Originator can access the full state for restoration method getFullState() { return this.fullState; // Full access granted to Originator } }

Caretaker Pseudocode

The Caretaker manages Mementos without knowledge of their internal structure, typically using a stack for undo operations via the narrow interface.

class Caretaker { private mementoStack = []; // Stack to hold Mementos for history method saveMemento(originator) { memento = originator.createMemento(); this.mementoStack.push(memento); // Store via narrow interface } method restoreMemento(originator) { if (!this.mementoStack.isEmpty()) { lastMemento = this.mementoStack.pop(); originator.restoreFromMemento(lastMemento); // Trigger restoration } } }

class Caretaker { private mementoStack = []; // Stack to hold Mementos for history method saveMemento(originator) { memento = originator.createMemento(); this.mementoStack.push(memento); // Store via narrow interface } method restoreMemento(originator) { if (!this.mementoStack.isEmpty()) { lastMemento = this.mementoStack.pop(); originator.restoreFromMemento(lastMemento); // Trigger restoration } } }

Full Example Flow

To demonstrate the pattern in action, consider an initialization and usage sequence where the Originator's state changes over operations, with saves and restores managed by the Caretaker. This flow highlights how the narrow interface protects the state from external modification while allowing seamless undo functionality.

// Initialize components originator = new Originator(); originator.setState("Initial state"); caretaker = new Caretaker(); // Perform initial save caretaker.saveMemento(originator); // Captures "Initial state" in Memento // Modify state originator.setState("Modified state 1"); caretaker.saveMemento(originator); // Captures "Modified state 1" // Further modification originator.setState("Modified state 2"); // Restore to previous state (undo last change) caretaker.restoreMemento(originator); // Restores "Modified state 1" using wide interface // Restore further (undo again) caretaker.restoreMemento(originator); // Restores "Initial state"

// Initialize components originator = new Originator(); originator.setState("Initial state"); caretaker = new Caretaker(); // Perform initial save caretaker.saveMemento(originator); // Captures "Initial state" in Memento // Modify state originator.setState("Modified state 1"); caretaker.saveMemento(originator); // Captures "Modified state 1" // Further modification originator.setState("Modified state 2"); // Restore to previous state (undo last change) caretaker.restoreMemento(originator); // Restores "Modified state 1" using wide interface // Restore further (undo again) caretaker.restoreMemento(originator); // Restores "Initial state"

In this pseudocode, comments denote the use of the narrow interface (e.g., createMemento() returns a Memento that hides fullState from the Caretaker) and wide interface (e.g., getFullState() is accessible only by the Originator to prevent encapsulation breaches).

Language-Specific Examples

Java Implementation

The Memento pattern in Java is typically implemented using classes that leverage the language's strong encapsulation features, such as private fields and inner classes, to protect the originator's internal state while allowing controlled access for restoration. This approach aligns with the pattern's description in the seminal work on design patterns, where the memento provides a narrow interface to the caretaker and a wide interface to the originator. A common example simulates a simple text editor's undo functionality, using a StringBuilder for mutable state. To enforce access control, the Memento is defined as a private inner class within the Originator (here, TextEditor), ensuring that only the originator can create or fully access the memento's state, while the Caretaker receives an opaque reference via a public interface. The Caretaker maintains a stack of mementos using a List for multiple undos.

java

import java.util.ArrayList; import java.util.List; // Public interface for narrow access by Caretaker interface Memento { // No methods exposed; opaque to Caretaker } // Originator class TextEditor { private StringBuilder text = new StringBuilder(); // Method to add text public void write(String input) { text.append(input); } // Save current state as Memento public Memento save() { return new ConcreteMemento(text.toString()); } // Restore from Memento public void restore(Memento memento) { if (memento instanceof ConcreteMemento) { text = new StringBuilder(((ConcreteMemento) memento).state); } } // Get current text (for demonstration) public String getText() { return text.toString(); } // Private inner class for wide interface (full state access) private static class ConcreteMemento implements Memento { private final String state; ConcreteMemento(String state) { this.state = state; } } } // Caretaker class History { private List<Memento> mementos = new ArrayList<>(); public void push(Memento memento) { mementos.add(memento); } public [boolean](/page/Boolean) [undo](/page/Undo)() { if (mementos.isEmpty()) { return false; } mementos.remove(mementos.size() - 1); return true; } public Memento pop() { if (mementos.isEmpty()) { return null; } return mementos.remove(mementos.size() - 1); } // Restore to a specific state (e.g., last saved) public void restoreLast(TextEditor editor) { if (!mementos.isEmpty()) { Memento last = mementos.get(mementos.size() - 1); editor.restore(last); } } } // Demonstration public class MementoDemo { public static void main(String[] args) { TextEditor editor = new TextEditor(); [History](/page/History) history = new History(); editor.write("Hello, "); System.out.println("After 'Hello, ': " + editor.getText()); history.push(editor.save()); // Save state 1 editor.write("World!"); System.out.println("After 'World!': " + editor.getText()); history.push(editor.save()); // Save state 2 editor.write(" How are you?"); System.out.println("After ' How are you?': " + editor.getText()); // Undo to state 2 history.restoreLast(editor); System.out.println("After undo to state 2: " + editor.getText()); // Undo to state 1 history.pop(); // Remove state 2 history.restoreLast(editor); System.out.println("After undo to state 1: " + editor.getText()); } }

import java.util.ArrayList; import java.util.List; // Public interface for narrow access by Caretaker interface Memento { // No methods exposed; opaque to Caretaker } // Originator class TextEditor { private StringBuilder text = new StringBuilder(); // Method to add text public void write(String input) { text.append(input); } // Save current state as Memento public Memento save() { return new ConcreteMemento(text.toString()); } // Restore from Memento public void restore(Memento memento) { if (memento instanceof ConcreteMemento) { text = new StringBuilder(((ConcreteMemento) memento).state); } } // Get current text (for demonstration) public String getText() { return text.toString(); } // Private inner class for wide interface (full state access) private static class ConcreteMemento implements Memento { private final String state; ConcreteMemento(String state) { this.state = state; } } } // Caretaker class History { private List<Memento> mementos = new ArrayList<>(); public void push(Memento memento) { mementos.add(memento); } public [boolean](/page/Boolean) [undo](/page/Undo)() { if (mementos.isEmpty()) { return false; } mementos.remove(mementos.size() - 1); return true; } public Memento pop() { if (mementos.isEmpty()) { return null; } return mementos.remove(mementos.size() - 1); } // Restore to a specific state (e.g., last saved) public void restoreLast(TextEditor editor) { if (!mementos.isEmpty()) { Memento last = mementos.get(mementos.size() - 1); editor.restore(last); } } } // Demonstration public class MementoDemo { public static void main(String[] args) { TextEditor editor = new TextEditor(); [History](/page/History) history = new History(); editor.write("Hello, "); System.out.println("After 'Hello, ': " + editor.getText()); history.push(editor.save()); // Save state 1 editor.write("World!"); System.out.println("After 'World!': " + editor.getText()); history.push(editor.save()); // Save state 2 editor.write(" How are you?"); System.out.println("After ' How are you?': " + editor.getText()); // Undo to state 2 history.restoreLast(editor); System.out.println("After undo to state 2: " + editor.getText()); // Undo to state 1 history.pop(); // Remove state 2 history.restoreLast(editor); System.out.println("After undo to state 1: " + editor.getText()); } }

For persistence across sessions, the inner ConcreteMemento class can implement the Serializable interface, allowing mementos to be written to disk or transmitted, though this requires marking the state field as transient if sensitive and handling of the StringBuilder content appropriately. In the demonstration above, running the main method outputs the progressive state changes and undos, simulating a where "Hello, World!" and then "Hello, " are restored step-by-step, illustrating the pattern's role in reversible operations without exposing internal details.

Python Implementation

The in Python leverages the language's dynamic typing and convention-based privacy (via single prefix for attributes) to encapsulate object state without strict access modifiers, allowing flexible snapshot through classes rather than inner classes or modules alone. A practical example illustrates this with a simple as the Originator, where the state represents the current numerical value, enabling operations like while supporting via mementos stored in a Caretaker. This approach aligns with Python's emphasis on and simplicity, avoiding boilerplate for visibility control. The Originator class maintains a private _value attribute and provides methods to perform calculations, create a memento capturing the current state, and restore from a memento. The Memento class holds the state privately, exposing it only to the Originator. The Caretaker manages a stack of mementos for operations.

python

class Memento: """Holds the state of the Originator.""" def __init__(self, state): self._state = state def get_state(self): return self._state class CalculatorOriginator: """Maintains the calculator state and operations.""" def __init__(self, initial_value=0): self._value = initial_value def add(self, amount): self._value += amount print(f"Current value: {self._value}") def create_memento(self): return Memento(self._value) def set_memento(self, memento): self._value = memento.get_state() print(f"Restored value: {self._value}") class Caretaker: """Manages mementos for undo functionality.""" def __init__(self): self._mementos = [] def save(self, memento): self._mementos.append(memento) print("State saved.") def undo(self): if self._mementos: restored_memento = self._mementos.pop() print("Undoing to previous state.") return restored_memento return None

class Memento: """Holds the state of the Originator.""" def __init__(self, state): self._state = state def get_state(self): return self._state class CalculatorOriginator: """Maintains the calculator state and operations.""" def __init__(self, initial_value=0): self._value = initial_value def add(self, amount): self._value += amount print(f"Current value: {self._value}") def create_memento(self): return Memento(self._value) def set_memento(self, memento): self._value = memento.get_state() print(f"Restored value: {self._value}") class Caretaker: """Manages mementos for undo functionality.""" def __init__(self): self._mementos = [] def save(self, memento): self._mementos.append(memento) print("State saved.") def undo(self): if self._mementos: restored_memento = self._mementos.pop() print("Undoing to previous state.") return restored_memento return None

In execution, the script demonstrates the by initializing a , performing additions with intermittent saves, and then invoking to states, showcasing stack-based management for multiple operations. For instance, starting from 0, adding 10 (save), adding 5 (save), then undoing twice (with the returned mementos passed to set_memento) restores the initial value, highlighting Python's straightforward handling for the memento stack without additional complexity. To use: caretaker.[undo](/page/Undo)() returns the memento, which is then passed to originator.set_memento(memento).

Applications and Consequences

Real-World Uses

The Memento pattern finds widespread application in graphical user interface (GUI) applications, particularly for implementing and redo functionality in document editors. For instance, in software like or , the pattern enables the capture of the application's state—such as text content, cursor position, layer configurations, or canvas modifications—at specific points, allowing users to revert to previous snapshots without exposing the internal representation of the document object. This approach ensures that complex GUI states can be preserved and restored efficiently, supporting multi-level operations that are essential for productivity tools. In game development, the Memento pattern is employed to save and restore player progress or AI decision states, facilitating features like move reversal or checkpoint reloading. Chess engines, for example, use it to store board configurations after each move, enabling the analysis of alternative paths or undoing invalid plays while maintaining the integrity of the game object's internal logic. Similarly, in broader game systems, it supports saving transient states for pause/resume mechanics or debugging simulations, as recommended in patterns for undo systems in grid-based or turn-based games. Transaction systems leverage the for rollbacks and state recovery, particularly in database operations where consistency must be maintained. Object-relational mapping (ORM) libraries draw inspiration from this to snapshot entity states before transactions, allowing restoration in case of failures without violating encapsulation of persistent objects. In versioning tools akin to , stashing functionality embodies the by capturing the current state into a memento-like snapshot, which can be reapplied later to resume work without altering the repository's core structure. Beyond these areas, the Memento pattern applies to pseudorandom number generators (PRNGs), where the serves as a memento to reproduce deterministic sequences from a prior state, ensuring reproducibility in simulations or testing environments. In embedded systems, it supports finite state machines (FSMs) by storing machine states for error recovery or , allowing restoration to a known configuration without exposing transition logic. This is particularly valuable in resource-constrained environments like IoT devices, where state persistence aids .

Benefits and Liabilities

The Memento pattern preserves encapsulation by allowing an object's internal state to be captured and externalized without exposing its implementation details to other objects, ensuring that only the originator and caretaker have appropriate access levels. This design simplifies the originator's responsibilities, as state management is delegated to the caretaker, reducing the complexity of the core object. Additionally, it supports flexible undo mechanisms by enabling the storage of multiple state snapshots, facilitating multi-level histories without altering the object's interface. Despite these advantages, the pattern incurs significant memory overhead, as each memento requires a complete copy of the object's state, which can become prohibitive for large objects or extensive histories, potentially scaling as O(n in storage for n states. costs arise from the time needed to create and restore these copies, particularly if states change frequently or involve substantial data. Ensuring memento integrity adds complexity, as the narrow interface for the caretaker must prevent tampering while the wide interface for the originator allows full access, and changes to the originator's implementation may invalidate existing mementos. The pattern balances transparency in state restoration with security through its dual-interface approach, though this may necessitate optimizations like shared states via other techniques in memory-constrained settings; however, the caretaker bears hidden costs in managing memento lifecycle without full knowledge of state size.

Command Pattern

The is a behavioral that encapsulates a request or operation as an object, enabling clients to parameterize, queue, log, or actions without direct coupling to the receiver performing the work. This approach transforms method invocations into standalone objects, supporting features like deferred execution, transaction support, and extensibility for operations such as macros or remote invocations. The synergizes with the particularly in implementing robust mechanisms, where Commands handle action execution and Mementos manage state preservation for reversal. In this combination, a Command object performs the intended operation on an originator while simultaneously creating a Memento to snapshot the pre-action state, allowing subsequent by restoring that state. For example, in graphical editors, a "draw line" Command executes the and stores a Memento of the canvas state beforehand, facilitating without recalculating changes. This integration leverages Command's action encapsulation with Memento's state isolation, often seen in frameworks for reversible user interfaces. Key differences lie in their core focuses: the emphasizes encapsulating and decoupling behavioral actions (such as execute, queue, or compose operations), whereas the Memento pattern centers on capturing and externalizing an object's internal state for later restoration without exposing its structure. Commands can embed Mementos internally to enable state-dependent undos, but they do not inherently handle state storage, relying on Memento for that when actions alter complex object graphs. A notable synergy example is in macro Commands, which sequence multiple sub-Commands; each sub-Command can store its own Memento, allowing the macro to support granular or full multi-step undos by selectively restoring states from the chain. This composition enhances behavioral flexibility in applications requiring history tracking, such as systems or interactive simulations.

Iterator Pattern

The is a behavioral that enables to the elements of an aggregate object, such as a list or collection, without exposing its underlying representation or internal structure. This abstraction allows clients to traverse complex data structures uniformly, regardless of whether they are implemented as arrays, linked lists, or trees, promoting flexibility and encapsulation in object-oriented designs. The Memento pattern is often used in conjunction with the , where an can use a Memento to capture the state of an iteration. The stores the Memento internally, allowing it to restore its position or roll back if necessary during traversal. This integration supports features like saving and resuming iteration progress without exposing the aggregate's internal details. While the focuses on sequential access and traversal of collections, the Memento emphasizes the creation and externalization of object snapshots for later restoration; their combination maintains , with Memento preserving state integrity for iterative processes.

References

Add your contribution
Related Hubs
User Avatar
No comments yet.