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 << " \t Listener: " << 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 << " \t Listener: " << 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