In this SSP (Stupidly-Simple-Patterns) series, I will highlight various useful but straightforward design patterns in C++. They worked in C++98, they still work in C++17! These patterns are more tools in your kit of design tools. After decades of use in various shapes and forms, they are proven to be reusable, making code flexible, robust and extensible in the long run. The topic of discussion for this article is – Observer Pattern.
The observer is a behavioral category pattern. That means it is a design construct which is known to program behavior for your program the certain way. This pattern is super useful design construct when you have an event-generating entity (subject) and one or more objects which listen and act on those events (observers). Subject to observer relation is always one-to-many. You may have multiple subjects as well in your program, though, each subject maintains its own observers. This pattern is also called a subscribe-observer pattern. Let’s go one by one into the structure of the subject and observer.
A typical observer would look like this:
Let's create concrete observers too.
The typical subject would look like this:
In normal circumstances, we will generally have only one subject and multiple observers. The example just demonstrates how it can be repeated with multiple subject-observer communication channels.
Now in our case, we have two subjects and two observers. Both our observers “observe” both subjects. This example may look complicated, but it shows one important point that your observer entity or object can observe more than one subject. Also, each subject always maintains a one-to-many relationship with multiple observers.
Typical duties for subject and observers
Duties for the subject are:
Provide an interface for registration for listening to one or more events for observers
Work on generating an event (or events)
Notify all the subscribed observers about the event through a callback
Duties for observer are:
Subscribe to subject through its public interface (this is a one-time task)
Anytime callback from subject comes then process the event as appropriate (ignoring event is also a legal action sometimes)
Now if the observer, like in this example case, if it is listening to more than one subject’s events, they will also need to find out which subject sent the event and did the callback before processing its event. Also, subject is typically not burdened with managing lifetime of the observer, that is done by client code which generally initializes both – subject and observer(s).
Now, lets put them in action with sample client code.
If you were to run this code, it would generate output like this:
Nice! We have not two subjects, two observers and both our observers subscribe/listen to both our subjects. In a typical multi-tasking environment, many-to-many communication channel like this is so common; there this pattern fits perfectly.
If your program has one or more subjects which generate events and you need client objects to listen in on those events, then observer pattern is the right design construct choice. Subject to observers relation is always one-to-many, though one observer can also listen to events from more than one subject.
Observer pattern simplifies such one-to-many communication by creating a broadcast channel between objects in your program, runtime. It makes complex object-to-object communication channel more structured runtime, and so makes the program more structured for code and so easy to understand. The easily understandable code is always easier to maintain.