1+ // Memento is a behavioral design pattern that lets you save and restore the previous state of an object
2+ // without violating encapsulation, captures and externalizes an object's internal state
3+ // Appicability:
4+ // (*) when you want to produce snapshots of the object’s state to be able to restore a previous state of the object.
5+ // (**) when direct access to the object’s fields/getters/setters violates its encapsulation.
6+ // UML: docs/uml/patterns_behavioral_memento.drawio.svg
7+
8+ #include < iostream>
9+ #include < string>
10+ #include < ctime> // for std::time
11+ #include < cstdlib> // for std::rand, std::srand
12+ #include < vector>
13+ #include < iomanip>
14+ #include < sstream>
15+ namespace
16+ {
17+ namespace Memento
18+ {
19+ /* *
20+ * Memento interface provides a way to retrieve the memento's metadata, such as creation date or name.
21+ * However, it doesn't expose the Originator's state.
22+ */
23+ class IMemento
24+ {
25+ public:
26+ virtual ~IMemento () = default ;
27+
28+ virtual std::string getName () const = 0;
29+ virtual std::string getDate () const = 0;
30+ virtual std::string getState () const = 0;
31+ };
32+
33+ /* *
34+ * Concrete Memento contains the infrastructure for storing the Originator's state.
35+ */
36+ class ConcreteMemento : public IMemento
37+ {
38+ private:
39+ std::string m_state;
40+ std::string m_date;
41+ std::string m_name;
42+
43+ public:
44+ explicit ConcreteMemento (const std::string &state) : m_state{state}
45+ {
46+ // Get current time
47+ std::time_t now = std::time (nullptr );
48+ std::tm *t = std::localtime (&now);
49+
50+ // Format date as YYYYMMDD_HHMMSS
51+ std::stringstream date_ss;
52+ date_ss << std::put_time (t, " %Y%m%d_%H%M%S" );
53+ m_date = date_ss.str ();
54+
55+ // Append a random number for uniqueness
56+ int rand_num = std::rand () % 10000 ; // optional: limit size
57+ std::stringstream name_ss;
58+ name_ss << " mem_" << m_date << " _" << rand_num;
59+ m_name = name_ss.str ();
60+ }
61+
62+ std::string getName () const override
63+ {
64+ return m_name;
65+ };
66+
67+ std::string getDate () const override
68+ {
69+ return this ->m_date ;
70+ };
71+
72+ std::string getState () const override
73+ {
74+ return this ->m_state ;
75+ }
76+ };
77+
78+ /* *
79+ * Originator holds some important state that may change over time.
80+ * It also defines a method for saving the state inside a memento and another method for
81+ * restoring the state from it.
82+ */
83+ class Originator
84+ {
85+ private:
86+ std::string m_state;
87+
88+ // Simulate new state using rand
89+ static std::string generateRandomString (int len = 10 )
90+ {
91+ // String literal concatenation
92+ const char alphaNum[] =
93+ " 0123456789"
94+ " ABCDEFGHIJKLMNOPQRSTUVWXYZ"
95+ " abcdefghijklmnopqrstuvwxyz" ;
96+
97+ int strLen = sizeof (alphaNum) - 1 ;
98+ std::string ranStr;
99+ for (int i = 0 ; i < len; ++i)
100+ {
101+ ranStr += alphaNum[std::rand () % strLen];
102+ }
103+ return ranStr;
104+ }
105+
106+ public:
107+ explicit Originator (const std::string &state) : m_state{state}
108+ {
109+ std::cout << " [O]Initial state is: " << this ->m_state << " \n " ;
110+ }
111+
112+ void operation ()
113+ {
114+ std::cout << " [O]Doing something important.\n " ;
115+ this ->m_state = this ->generateRandomString (30 );
116+ std::cout << " [O]The state has changed to: " << this ->m_state << " \n " ;
117+ }
118+
119+ // Save the current state inside a memento.
120+ IMemento *save ()
121+ {
122+ return new ConcreteMemento (this ->m_state );
123+ }
124+
125+ // Restores the Originator's state from a memento object.
126+ void restore (IMemento *mem)
127+ {
128+ this ->m_state = mem->getState ();
129+ std::cout << " [O]The state has restored to: " << this ->m_state << " \n " ;
130+
131+ delete mem;
132+ }
133+ };
134+
135+ /* *
136+ * The Caretaker doesn't depend on the Concrete Memento class. Therefore, it
137+ * doesn't have access to the originator's state, stored inside the memento. It
138+ * works with all mementos via the base Memento interface.
139+ */
140+ class CareTaker
141+ {
142+ private:
143+ std::vector<IMemento *> m_mementos;
144+ Originator *m_originator;
145+
146+ public:
147+ explicit CareTaker (Originator *const org) : m_originator{org} {}
148+ ~CareTaker ()
149+ {
150+ for (IMemento *m : m_mementos)
151+ {
152+ delete m;
153+ }
154+ }
155+
156+ void backup ()
157+ {
158+ std::cout << " [C]Saving Originator's state...\n " ;
159+ this ->m_mementos .push_back (this ->m_originator ->save ());
160+ }
161+
162+ void undo ()
163+ {
164+ if (this ->m_mementos .size () != 0 )
165+ {
166+ IMemento *mem = m_mementos.back ();
167+ this ->m_mementos .pop_back ();
168+
169+ std::cout << " [C]Restoring state to: " << mem->getName () << " \n " ;
170+ this ->m_originator ->restore (mem);
171+ }
172+ }
173+
174+ void history () const
175+ {
176+ std::cout << " [C]The list of mementos:\n " ;
177+ for (const IMemento *m : m_mementos)
178+ {
179+ std::cout << " \t " << m->getName () << " \n " ;
180+ }
181+ }
182+ };
183+
184+ namespace Client
185+ {
186+ void clientCode (Originator *const org)
187+ {
188+ CareTaker *careTaker = new CareTaker (org);
189+
190+ // 1
191+ careTaker->backup ();
192+ org->operation ();
193+
194+ // 2
195+ careTaker->backup ();
196+ org->operation ();
197+
198+ // 3
199+ careTaker->backup ();
200+ org->operation ();
201+
202+ careTaker->history ();
203+ careTaker->undo ();
204+ careTaker->undo ();
205+ careTaker->history ();
206+ careTaker->undo ();
207+
208+ // [P] The previous state can’t be restored directly because m_state is private.
209+ // We need a Memento to save and recover internal state safely.
210+ }
211+ }
212+
213+ void run ()
214+ {
215+ // Gen seed
216+ std::srand (static_cast <unsigned int >(std::time (NULL )));
217+
218+ Originator *origin = new Originator (" Hello World" );
219+ Client::clientCode (origin);
220+
221+ delete origin;
222+ }
223+ }
224+ }
225+
226+ struct MementoAutoRunner
227+ {
228+ MementoAutoRunner ()
229+ {
230+ std::cout << " \n --- Memento Pattern Example ---\n " ;
231+ Memento::run ();
232+ }
233+ };
234+
235+ static MementoAutoRunner instance;
0 commit comments