Open In App

Memento Design Pattern | C++ Design Patterns

Last Updated : 05 Jan, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Memento Design Pattern is a behavioral design pattern that provides a mechanism for capturing an object’s internal state and restoring it to that state at a later time. This pattern is useful when we need to implement features like undo/redo functionality or when we want to save and restore an object’s state for various reasons.

Momento

Example for Memento Design Pattern in C++:

Problem Statement:

Suppose we have to represent two states by the use of memento patterns then we can use three classes to represent this Originator class to store the data then memento patterns for storing the state of originator then caretaker will hold the memento to solve the problem.

Key Component of Memento Method:

The Memento pattern aims to capture an object’s internal state, so it can be restored to that state later.

  • Originator: This is the object whose state we want to capture. The Originator creates a Memento object to save its state and uses it to restore its state.
  • Memento: This is an object that stores the state of the Originator. It has two primary interfaces, getState() returns the saved stateand setState(state) sets the state to a specific value.
  • Caretaker: This is responsible for holding and managing Memento objects. It doesn’t modify the Memento but can save and restore the state of the Originator.

Step wise Implementation of above Example:

So In the above example, we have an Originator class that has an internal state, which can be saved and restored using the Memento pattern. The Caretaker class is responsible for managing the Memento objects, allowing you to save and restore the Originator’s state.

Originator: This is the object whose state we want to save and restore.

C++




class Originator {
private:
    std::string state;
 
public:
    void SetState(const std::string& newState) {
        state = newState;
    }
 
    std::string GetState() const {
        return state;
    }
 
    Memento SaveStateToMemento() {
        return Memento(state);
    }
 
    void RestoreStateFromMemento(const Memento& memento) {
        state = memento.GetState();
    }
};


Memento: This class represents the saved state of the Originator.

C++




class Memento {
private:
    std::string state;
 
public:
    Memento(const std::string& stateToSave) : state(stateToSave) {}
 
    std::string GetState() const {
        return state;
    }
};


Caretaker: The Caretaker is responsible for storing and managing the Mementos.

C++




class Caretaker {
private:
    std::vector<Memento> mementos;
 
public:
    void AddMemento(const Memento& memento) {
        mementos.push_back(memento);
    }
 
    Memento GetMemento(int index) const {
        if (index >= 0 && index < mementos.size()) {
            return mementos[index];
        }
        throw std::out_of_range("Invalid memento index");
    }
};


Then in the main class we use all these stored classes to get the output.

Overall Code for above Example:

C++




#include <iostream>
#include <string>
#include <vector>
 
// Originator: The object whose state needs to be saved and restored.
class Originator {
private:
    std::string state;
 
public:
    void SetState(const std::string& newState) {
        state = newState;
    }
 
    std::string GetState() const {
        return state;
    }
 
    // Memento: Inner class representing the state of the Originator.
    class Memento {
    private:
        std::string state;
 
    public:
        Memento(const std::string& originatorState) : state(originatorState) {}
 
        std::string GetSavedState() const {
            return state;
        }
    };
 
    // Create a Memento object to save the current state.
    Memento CreateMemento() const {
        return Memento(state);
    }
 
    // Restore the state from a Memento object.
    void RestoreState(const Memento& memento) {
        state = memento.GetSavedState();
    }
};
 
// Caretaker: Manages the Memento objects.
class Caretaker {
private:
    std::vector<Originator::Memento> mementos;
 
public:
    void AddMemento(const Originator::Memento& memento) {
        mementos.push_back(memento);
    }
 
    Originator::Memento GetMemento(int index) const {
        if (index >= 0 && index < mementos.size()) {
            return mementos[index];
        }
        throw std::out_of_range("Invalid Memento index");
    }
};
 
int main() {
    Originator originator;
    Caretaker caretaker;
 
    originator.SetState("State 1");
    caretaker.AddMemento(originator.CreateMemento());
 
    originator.SetState("State 2");
    caretaker.AddMemento(originator.CreateMemento());
 
    // Restore to the previous state
    originator.RestoreState(caretaker.GetMemento(0));
    std::cout << "Current state: " << originator.GetState() << std::endl;
 
    // Restore to an even earlier state
    originator.RestoreState(caretaker.GetMemento(1));
    std::cout << "Current state: " << originator.GetState() << std::endl;
 
    return 0;
}


Output

Current state: State 1
Current state: State 2






Diagrammatic Representation of Memento Method in C++:

M

Diagrammatic Representation of Momento Design Pattern in C++

In this example, we create an Originator object, change its state, and save the state into the Caretaker using the SaveStateToMemento method. Later, we can restore the state using the RestoreStateFromMemento method. The Caretaker is responsible for storing and managing the Mementos, allowing us to go back to previous states of the Originator.

Advantages of Memento Pattern in C++:

  • Easy State Management: Managing an object’s state history becomes straightforward with the Memento Pattern. It allows us to save and restore the object’s state at different points in time without cluttering the object’s interface or code.
  • Encapsulation of Object State: The Memento Pattern encapsulates an object’s state within a separate Memento object. This ensures that the object’s state is not directly accessible from external classes, maintaining the principle of encapsulation and information hiding.
  • Support for Undo and Redo: The Memento Pattern is widely used to implement undo and redo functionality in applications. By storing and managing a history of an object’s states, we can easily revert the object to a previous state or move forward to a more recent state, providing a seamless user experience.
  • Snapshot and Rollback: The pattern enables us to take snapshots of an object’s state at specific moments, which can be valuable for debugging and analysis. we can also rollback an object to a previous state to troubleshoot or examine issues.
  • Flexibility: The pattern is flexible and can be adapted to various use cases. we can implement it with different levels of granularity, from saving the entire object state to just specific parts of it.
  • Maintains Object Integrity: By encapsulating the state management logic within the Memento, the Memento Pattern helps maintain the integrity of the object. It prevents accidental modification of the object’s state outside of the intended mechanisms.
  • Extensibility: We can extend the Memento Pattern to implement additional features. For example, we can add features like version control, branching, or sharing object states with other objects or users.
  • Improved Testing and Debugging: With the ability to capture and restore object states, the Memento Pattern can aid in testing and debugging by allowing us to isolate specific states for testing or analysis.

The Memento Pattern is a valuable tool for managing and preserving the state of objects in a way that enhances code modularity, flexibility, and maintainability, making it particularly useful in applications where state management is critical.

Disadvantages of Memento pattern in C++:

  • Increased Memory Usage: Storing the state of an object requires additional memory. This can become a concern if we are working with large objects or a large number of objects, as it may lead to increased memory usage.
  • Performance Overhead: The process of creating and restoring states can introduce some performance overhead. This is particularly true if the state is complex or if there are frequent state changes, as the system needs to manage and store multiple states.
  • Encapsulation Violation: In order to capture and restore the internal state of an object, the Memento pattern may require exposing the internal details of the object. This can violate the principles of encapsulation, making the object’s implementation details accessible to external classes.
  • Versioning and Compatibility: If the internal structure of the object changes, the Memento pattern may face challenges in terms of versioning and compatibility. This can be an issue when trying to restore the state of an object saved with an older version of the Memento.
  • Complexity in Implementation: Implementing the Memento pattern may introduce additional complexity to the codebase. Managing the creation, storage, and retrieval of mementos can be intricate, especially in larger systems.
  • Storage Management: Managing the storage of multiple mementos can become challenging, especially if there is a need to limit the number of stored states or if there are constraints on storage resources.
  • Dependency on Originator: The Memento pattern creates a dependency between the originator (the object whose state is being managed) and the memento (the object representing the state). Changes in the originator may require corresponding changes in the memento, and this tight coupling can impact system flexibility.


Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads