Memento Design Pattern in C++

Cengizhan Varlı
5 min readNov 8, 2023

--

We often need to return our variables to their previous state. For example, as above, we may need to return a variable of the class to its previous state If certain conditions are true. Most of us keep the previous value of the relevant variable for such situations when writing software. When we want to return the variable to its previous value, we assign the previous value we previously held to our variable. Yes, This technique is not unreasonable.

What if we want to return the entire class to its previous state, not just a single variable? or if we want to return not only to the previous state but to a much earlier state. Let’s not tire ourselves by trying to come up with various solutions. We are not the first people to encounter this problem. Many software developers have had such a need before and a pattern has formed for this. This is Memento Design Pattern.

The Memento design pattern is a behavioral design pattern used in software engineering. It is primarily used to capture an object’s internal state at a specific point in time, allowing you to restore the object to that state later. This pattern is useful when you need to implement features like undo/redo functionality or the ability to revert an object to a previous state.

The Memento pattern typically involves three main roles:

  • Originator: This is the object whose state you want to save. The Originator creates a Memento object to store its state or can restore its state from a Memento object.
  • Memento: The Memento object is responsible for storing the state of the Originator. It doesn’t expose the state to other objects. It typically has methods to get and set the state of the Originator.
  • Caretaker: The Caretaker is responsible for keeping track of the Memento objects and their history. It can save and retrieve Memento objects from a collection. It doesn’t modify the Memento’s state but can request the Originator to save or restore its state using a Memento.

Private member variables of memento class and originator class must be the same.

#include <iostream>
#include <string>
#include <memory>

class AddressBookMemento {
public:
AddressBookMemento(const std::string& name, const std::string& surname, const std::string& tel)
: _name(name), _surname(surname), _tel(tel) {}

const std::string& GetName() const { return _name; }
const std::string& GetSurname() const { return _surname; }
const std::string& GetTel() const { return _tel; }

private:
std::string _name;
std::string _surname;
std::string _tel;
};

class AddressBook {
public:
AddressBook(const std::string& name, const std::string& surname, const std::string& tel)
: _name(name), _surname(surname), _tel(tel) {}

std::unique_ptr<AddressBookMemento> CreateMemento() {
return std::make_unique<AddressBookMemento>(_name, _surname, _tel);
}

void RestoreFromMemento(const AddressBookMemento& memento) {
_name = memento.GetName();
_surname = memento.GetSurname();
_tel = memento.GetTel();
}

void Display() const {
std::cout << "Name: " << _name << ", Surname: " << _surname << ", Tel: " << _tel << std::endl;
}

void SetContactInfo(const std::string& name, const std::string& surname, const std::string& tel) {
_name = name;
_surname = surname;
_tel = tel;
}

private:
std::string _name;
std::string _surname;
std::string _tel;
};

int main() {
AddressBook addressBook("Cengizhan", "Varli", "123456789");

addressBook.Display();

// Create momento and make changes
auto memento = addressBook.CreateMemento();
addressBook.SetContactInfo("Burak", "Erkan", "987654321");
addressBook.Display();

// Return Previous State
addressBook.RestoreFromMemento(*memento);
addressBook.Display();

return 0;
}

AddressBook can save and restore the state of contact information, including name, surname, and telephone number.

AddressBookMemento Class:

  • This class represents the Memento in the Memento design pattern.
  • It stores the state of the AddressBook class, including _name,_surname, and _tel.
  • The constructor takes the initial values for these attributes and initializes them.
  • It provides getter methods to retrieve the values of these attributes.

AddressBook Class:

  • This class represents the Originator in the Memento design pattern.
  • It manages contact information, which includes _name, _surname, and _tel.
  • The constructor initializes these attributes with the provided values.
  • CreateMemento() method is used to create a Memento object that captures the current state of the AddressBook.
  • RestoreFromMemento() method allows you to restore the AddressBook to a previous state using a provided Memento.
  • Display method is used to display the current contact information.
  • SetContactInfo method is used to update the contact information.

main Function:

  • In the main function, an AddressBook object named addressBook is created with initial contact information.
  • A Memento is created by calling CreateMemento(), capturing the initial state of the addressBook.
  • Contact information is modified using the SetContactInfo method, and the updated state is displayed.
  • Finally, the RestoreFromMemento() method is called with the Memento to revert the addressBook to its previous state, and the original state is displayed.

Let’s make the design a little more difficult :)

Let’s try to return to the previous states of our variables, not the previous one.

#include <iostream>
#include <string>
#include <memory>
#include <vector>

class AddressBookMemento {
public:
AddressBookMemento(const std::string& name, const std::string& surname, const std::string& tel)
: name_(name), surname_(surname), tel_(tel) {}

const std::string& GetName() const { return name_; }
const std::string& GetSurname() const { return surname_; }
const std::string& GetTel() const { return tel_; }

private:
std::string name_;
std::string surname_;
std::string tel_;
};

class AddressBook {
public:
AddressBook(const std::string& name, const std::string& surname, const std::string& tel)
: _name(name), _surname(surname), _tel(tel) { }

void RestoreFromMemento(const AddressBookMemento& memento) {
_name = memento.GetName();
_surname = memento.GetSurname();
_tel = memento.GetTel();
}

void Display() const {
std::cout << "Name: " << _name << ", Surname: " << _surname << ", Tel: " << _tel << std::endl;
}

void SetContactInfo(const std::string& name, const std::string& surname, const std::string& tel) {
_history.push_back(std::make_unique<AddressBookMemento>(_name,_surname,_tel));

_name = name;
_surname = surname;
_tel = tel;
}

void RestoreToSpecificMemento(size_t index) {
if (index < _history.size()) {
RestoreFromMemento(*_history[index]);
}
}

private:
std::string _name;
std::string _surname;
std::string _tel;
std::vector<std::unique_ptr<AddressBookMemento>> _history;
};

int main() {
AddressBook addressBook("Cengizhan", "Varli", "123456789");

addressBook.Display();

addressBook.SetContactInfo("Burak", "Erkan", "987654321");
addressBook.Display();

addressBook.SetContactInfo("Burhan", "Koksaldi", "555555555");
addressBook.Display();

addressBook.RestoreToSpecificMemento(0);
addressBook.Display();

return 0;
}

Here we perform our set operations via the SetContactInfo() function.

In this way, we can access the old values ​​through the vector.

Note: Of course, it is always possible to write better software.

--

--

Responses (1)