Skip to content

Commit a2da371

Browse files
committed
Add the Observer pattern
1 parent 4fd7c52 commit a2da371

3 files changed

Lines changed: 209 additions & 0 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ set(APP_SOURCES
107107
"src/patterns/behavioral/TemplateMethod.cpp"
108108
"src/patterns/behavioral/Strategy.cpp"
109109
"src/patterns/behavioral/State.cpp"
110+
"src/patterns/behavioral/Observer.cpp"
110111
)
111112

112113
# Test files

docs/uml/patterns_behavioral_observer.drawio.svg

Lines changed: 4 additions & 0 deletions
Loading
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;

0 commit comments

Comments
 (0)