Skip to content

Commit 78a75b0

Browse files
authored
Merge pull request #22 from urboob21/dev_branch
id 1763902952
2 parents 5324abc + a2da371 commit 78a75b0

8 files changed

Lines changed: 514 additions & 0 deletions

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ set(APP_SOURCES
105105
"src/patterns/behavioral/Memento.cpp"
106106
"src/patterns/behavioral/Visitor.cpp"
107107
"src/patterns/behavioral/TemplateMethod.cpp"
108+
"src/patterns/behavioral/Strategy.cpp"
109+
"src/patterns/behavioral/State.cpp"
110+
"src/patterns/behavioral/Observer.cpp"
108111
)
109112

110113
# Test files

docs/uml/patterns_behavioral_observer.drawio.svg

Lines changed: 4 additions & 0 deletions
Loading

docs/uml/patterns_behavioral_state.drawio.svg

Lines changed: 4 additions & 0 deletions
Loading

docs/uml/patterns_behavioral_strategy.drawio.svg

Lines changed: 4 additions & 0 deletions
Loading

docs/uml/pattern_behavioral_templatemethod.drawio.svg renamed to docs/uml/patterns_behavioral_templatemethod.drawio.svg

File renamed without changes.
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
// Observer is a behavioral design pattern that lets you define a subscription mechanism
2+
// to notify multiple objects about any events that happen to the object they’re observing.
3+
// Usage examples: The most popular usage of the Observer pattern in C++ code is facilitating communications between GUI components of an app.
4+
// The synonym of the Observer is the `Controller` part of MVC pattern.
5+
// Appicability:
6+
// (*) when changes to the state of one object may require changing other objects, and the actual set of objects is unknown beforehand or changes dynamically.
7+
// (**) when some objects in your app must observe others, but only for a limited time or in specific cases.
8+
9+
// UML: docs/uml/patterns_behavioral_observer.drawio.svg
10+
11+
#include <iostream>
12+
#include <string>
13+
#include <list>
14+
15+
namespace
16+
{
17+
namespace Observer
18+
{
19+
enum class Event
20+
{
21+
CREATE = 0,
22+
READ,
23+
UPDATE,
24+
DELETE,
25+
};
26+
27+
static inline const char *getEventName(const Event &e)
28+
{
29+
switch (e)
30+
{
31+
case Event::CREATE:
32+
return "CREATE";
33+
case Event::READ:
34+
return "READ";
35+
case Event::UPDATE:
36+
return "UPDATE";
37+
case Event::DELETE:
38+
return "DELETE";
39+
}
40+
return "invalid_event";
41+
}
42+
43+
/**
44+
* IObserver aka Subscriber
45+
* The Subscriber interface declares the notification interface.
46+
* In most cases, it consists of a single update method.
47+
* The method may have several parameters that let the publisher pass some event details along with the update.
48+
* E.g. Event Listen to UI events
49+
*/
50+
class IListenerObserver
51+
{
52+
public:
53+
virtual ~IListenerObserver() = default;
54+
55+
// update
56+
virtual void update(const Event e) = 0;
57+
};
58+
59+
/**
60+
* Subject aka Publisher
61+
* The Publisher issues events of interest to other objects.
62+
* These events occur when the publisher changes its state or executes some behaviors.
63+
* Publishers contain a subscription infrastructure that lets new subscribers join and current subscribers leave the list.
64+
*
65+
* E.g Widget dispatches click events to observers
66+
*/
67+
class IWidgetSubject
68+
{
69+
public:
70+
virtual ~IWidgetSubject() {};
71+
// addListener
72+
virtual void attach(IListenerObserver *observer) = 0;
73+
// removeLister
74+
virtual void detach(IListenerObserver *observer) = 0;
75+
// e.g.click
76+
virtual void notify(const Event &e) = 0;
77+
};
78+
79+
class ButtonConcreteSubject : public IWidgetSubject
80+
{
81+
private:
82+
std::list<IListenerObserver *> m_listeners;
83+
84+
public:
85+
void attach(IListenerObserver *observer) override
86+
{
87+
m_listeners.push_back(observer);
88+
}
89+
90+
void detach(IListenerObserver *observer) override
91+
{
92+
m_listeners.remove(observer);
93+
}
94+
95+
void notify(const Event &e) override
96+
{
97+
std::cout << "[Subject] notify event-" << getEventName(e) << "\n";
98+
for (IListenerObserver *o : m_listeners)
99+
{
100+
o->update(e);
101+
}
102+
}
103+
};
104+
105+
class AbstractListenerObserver : public IListenerObserver
106+
{
107+
private:
108+
int m_num;
109+
inline static int num_observers = 0;
110+
111+
protected:
112+
void log(const Event &e) const
113+
{
114+
std::cout << "\t-id:" << m_num << "-event:" << getEventName(e) << "\n";
115+
}
116+
117+
public:
118+
explicit AbstractListenerObserver()
119+
{
120+
m_num = ++num_observers;
121+
}
122+
};
123+
124+
/**
125+
* Concrete Subscribers perform some actions in response to notifications issued by the publisher.
126+
* All of these classes must implement the same interface so the publisher isn’t coupled to concrete classes.
127+
*/
128+
class ConcreteListenerObserverA : public AbstractListenerObserver
129+
{
130+
private:
131+
static const inline char *type = "A-type";
132+
133+
public:
134+
void update(const Event e) override
135+
{
136+
std::cout << "\tListener: " << type;
137+
log(e);
138+
}
139+
};
140+
141+
class ConcreteListenerObserverB : public AbstractListenerObserver
142+
{
143+
private:
144+
static const inline char *type = "B-type";
145+
146+
public:
147+
void update(const Event e) override
148+
{
149+
std::cout << "\tListener: " << type;
150+
log(e);
151+
}
152+
};
153+
154+
/**
155+
* The Client creates publisher and subscriber objects separately
156+
* and then registers subscribers for publisher updates.
157+
*/
158+
namespace Client
159+
{
160+
void clientCode(IWidgetSubject *const s)
161+
{
162+
s->notify(Event::UPDATE);
163+
}
164+
}
165+
166+
void run()
167+
{
168+
IWidgetSubject *btn = new ButtonConcreteSubject();
169+
170+
IListenerObserver *listener_1 = new ConcreteListenerObserverA();
171+
IListenerObserver *listener_2 = new ConcreteListenerObserverA();
172+
IListenerObserver *listener_3 = new ConcreteListenerObserverA();
173+
IListenerObserver *listener_4 = new ConcreteListenerObserverB();
174+
175+
btn->attach(listener_1);
176+
btn->attach(listener_2);
177+
btn->attach(listener_3);
178+
btn->attach(listener_4);
179+
Client::clientCode(btn);
180+
181+
std::cout << "Remove listener2\n";
182+
btn->detach(listener_2);
183+
Client::clientCode(btn);
184+
185+
delete btn;
186+
delete listener_1;
187+
delete listener_2;
188+
delete listener_3;
189+
delete listener_4;
190+
}
191+
192+
}
193+
}
194+
195+
struct ObserverAutoRunner
196+
{
197+
ObserverAutoRunner()
198+
{
199+
std::cout << "\n--- Observer Pattern Example ---\n";
200+
Observer::run();
201+
}
202+
};
203+
204+
static ObserverAutoRunner instance;

src/patterns/behavioral/State.cpp

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// State is a behavioral design pattern that lets an object alter its behavior when its internal state changes.
2+
// It appears as if the object changed its class.
3+
// Appicability:
4+
// (*) when you have an object that behaves differently depending on its current state
5+
// , the number of states is enormous, and the state-specific code changes frequently.
6+
// (**) when you have a class polluted with massive conditionals that alter
7+
// how the class behaves according to the current values of the class’s fields.
8+
// (***) when you have a lot of duplicate code across similar states and transitions of a condition-based state machine.
9+
// UML: docs/uml/patterns_behavioral_state.drawio.svg
10+
11+
#include <iostream>
12+
#include <string>
13+
namespace
14+
{
15+
namespace State
16+
{
17+
class DeviceContext;
18+
19+
/**
20+
* The State interface declares the state-specific methods.
21+
* These methods should make sense for all concrete states because you don’t want some of your states
22+
* to have useless methods that will never be called.
23+
*/
24+
class IState
25+
{
26+
public:
27+
virtual ~IState() = default;
28+
virtual void setContext(DeviceContext *ctx) = 0;
29+
virtual void handle() = 0;
30+
};
31+
32+
/*
33+
* To avoid duplication of similar code across multiple states, you may provide intermediate abstract classes
34+
* that encapsulate some common behavior.
35+
*/
36+
class AbstractState : public IState
37+
{
38+
protected:
39+
DeviceContext *m_ctx;
40+
41+
public:
42+
void setContext(DeviceContext *ctx) override
43+
{
44+
this->m_ctx = ctx;
45+
}
46+
};
47+
48+
/**
49+
* Context stores a reference to one of the concrete state objects and delegates to it all state-specific work.
50+
* The context communicates with the state object via the state interface.
51+
* The context exposes a setter for passing it a new state object.
52+
*/
53+
class DeviceContext
54+
{
55+
private:
56+
IState *m_state;
57+
58+
public:
59+
explicit DeviceContext(IState *state) : m_state{nullptr}
60+
{
61+
this->changeState(state);
62+
}
63+
64+
~DeviceContext()
65+
{
66+
delete m_state;
67+
}
68+
69+
void changeState(IState *state)
70+
{
71+
std::cout << "[DeviceContext]: Changing state\n";
72+
if (this->m_state != nullptr)
73+
{
74+
delete this->m_state;
75+
}
76+
this->m_state = state;
77+
this->m_state->setContext(this);
78+
}
79+
80+
void operation()
81+
{
82+
this->m_state->handle();
83+
}
84+
};
85+
86+
/**
87+
* Concrete States provide their own implementations for the state-specific methods.
88+
*/
89+
class IdeConcreteState : public AbstractState
90+
{
91+
public:
92+
void handle() override;
93+
};
94+
95+
class ProcessingConcreteState : public AbstractState
96+
{
97+
public:
98+
void handle() override;
99+
};
100+
101+
class ErrorConcreteState : public AbstractState
102+
{
103+
public:
104+
void handle() override
105+
{
106+
std::cout << "[Error] Device error! Reset required.\n";
107+
108+
// After recover => go Idle
109+
this->m_ctx->changeState(new IdeConcreteState());
110+
}
111+
};
112+
113+
void IdeConcreteState::handle()
114+
{
115+
std::cout << "[Ide] Device is idle. Waiting...\n";
116+
this->m_ctx->changeState(new ProcessingConcreteState());
117+
}
118+
119+
void ProcessingConcreteState::handle()
120+
{
121+
std::cout << "[Processing] Processing data...\n";
122+
bool ok = true; // Example processing result
123+
static int index = 0;
124+
index++;
125+
if (index % 2 == 0)
126+
{
127+
ok = true;
128+
}
129+
else
130+
{
131+
ok = false;
132+
}
133+
if (ok)
134+
{
135+
// Back to idle after finishing job
136+
this->m_ctx->changeState(new IdeConcreteState());
137+
}
138+
else
139+
{
140+
this->m_ctx->changeState(new ErrorConcreteState());
141+
}
142+
}
143+
144+
namespace Client
145+
{
146+
void clientCode(DeviceContext *const device)
147+
{
148+
device->operation();
149+
}
150+
}
151+
void run()
152+
{
153+
DeviceContext *device = new DeviceContext(new IdeConcreteState());
154+
for (int loopIdx = 0; loopIdx < 10; ++loopIdx)
155+
Client::clientCode(device);
156+
delete device;
157+
}
158+
}
159+
}
160+
161+
struct StateAutoRunner
162+
{
163+
StateAutoRunner()
164+
{
165+
std::cout << "\n--- State Pattern Example ---\n";
166+
State::run();
167+
}
168+
};
169+
170+
static StateAutoRunner instance;

0 commit comments

Comments
 (0)