Skip to content

Commit f2a835c

Browse files
authored
Merge pull request #19 from urboob21/dev_branch
id 1763735936
2 parents 8816ce4 + 069a6c5 commit f2a835c

File tree

3 files changed

+240
-0
lines changed

3 files changed

+240
-0
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ set(APP_SOURCES
102102
"src/patterns/behavioral/Command.cpp"
103103
"src/patterns/behavioral/Iterator.cpp"
104104
"src/patterns/behavioral/Mediator.cpp"
105+
"src/patterns/behavioral/Memento.cpp"
105106
)
106107

107108
# Test files

docs/uml/patterns_structural_memento.drawio.svg

Lines changed: 4 additions & 0 deletions
Loading
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
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

Comments
 (0)