Of course you have heard about Observer Design Pattern in C++. This is again, like the Singleton, one of the better known design patterns.
To start this off, just like in my last article about “Singleton Design Pattern in C++”, I want to once again recommend the book Modern C++ Design: Generic Programming and Design Patterns Applied by Andrei Alexandrescu (which is one of the co-founders of boost).
What is Observer?
Observer is a behavioral design pattern that allows some objects to notify other objects about changes in their state.
Basically, if you have inter dependencies and some objects need to know when other objects changed their state, then you probably want to implement the Observer design pattern.
It is very common and mostly found in implementation of GUI frameworks – for e.g. an object which has logic about a button will observe the actual button and will be notified when the button is pressed.
It has two important parts in the implementation:
- Observer/Subscriber – the one “observing” or being notified about changes
- Observable/Subject – the “observed” or the one that notifies others about changes
Pros and cons
Pros:
- You can have loose coupling between objects that interact with each other
- It allows sending data to other objects without any changes to the Subject or Observer classes
- You can add/remove Observers at any point
Cons:
- It can add unnecessary complexity to the application
- It needs to involve inheritance
- The Observer is notified in random order by the Subjects
Observer Implementation
We are going to use “IObserver” and “Subject” classes in this case. The naming, as I already mentioned above, can differ. It is mostly a preference issue.
We will also have “SomeObserverImpl” and “SomeSubjectImpl” classes in this example, in order to show you how it should be used.
IObserver.h
#ifndef I_OBSERVER_H_
#define I_OBSERVER_H_
#include <string>
class IObserver
{
public:
// Message type that the observer can send
// This can be any type you want
// It is not even necessary, you can just notify (without any message or data)
using Message = std::string;
virtual ~IObserver() {}
// Method to update the state with message from Subjects
// When Subjects notify -> this function will be called with the message
virtual void update(const Message& message) = 0;
};
#endif // I_OBSERVER_H_
Subject.h
#ifndef SUBJECT_H_
#define SUBJECT_H_
#include <list>
#include "IObserver.h"
class Subject
{
public:
virtual ~Subject() {}
// Add new Observer that current Subject should notify
void registerObserver(IObserver*);
// Remove Observer so it no longer gets notified by current Subject
void unregisterObserver(IObserver*);
protected:
// Notify all the Observers registered with your message
void notify(const IObserver::Message& message);
private:
std::list<IObserver*> observers_;
};
#endif // SUBJECT_H_
Subject.cpp
#include <algorithm>
#include "IObserver.h"
#include "Subject.h"
void Subject::registerObserver(IObserver* obs)
{
observers_.push_back(obs);
}
void Subject::unregisterObserver(IObserver* obs)
{
observers_.remove(obs);
}
void Subject::notify(const IObserver::Message& message)
{
std::for_each(observers_.begin(), observers_.end(), [&message](auto it){ it->update(message); });
}
So, here you have it. This is the Observer implementation. You can use it directly with your own implementation but we are also going to implement an example here so you can see better how it’s done.
SomeObserverImpl.h
#ifndef SOME_OBSERVER_IMPL_H_
#define SOME_OBSERVER_IMPL_H_
#include <string>
#include "IObserver.h"
class SomeObserverImpl : public IObserver
{
public:
SomeObserverImpl(const std::string& tag) : tag_(tag) {};
private:
// Some bussiness logic on notification from Subjects
void update(const IObserver::Message& message ) override;
std::string tag_;
};
#endif // SOME_OBSERVER_IMPL_H_
SomeObserverImpl.cpp
#include <iostream>
#include "SomeObserverImpl.h"
void SomeObserverImpl::update(const IObserver::Message& message)
{
std::cout << "\nReceived notification on " << tag_ << " from Subject with message " << message << "\n";
}
SomeSubjectImpl.h
#ifndef SOME_SUBJECT_IMPL_H_
#define SOME_SUBJECT_IMPL_H_
#include "Subject.h"
class SomeSubjectImpl : public Subject
{
public:
// Subject bussiness logic and notification afterwards
void doBussinessLogicAndNotify(const std::string& message);
};
#endif // SOME_SUBJECT_IMPL_H_
SomeSubjectImpl.cpp
#include <iostream>
#include "SomeSubjectImpl.h"
void SomeSubjectImpl::doBussinessLogicAndNotify(const std::string& message)
{
std::cout << "\nDoing my own bussiness logic on Subject first.\n";
std::cout << "\nNotifying observers!\n";
// Notify all observers with message
notify(message);
}
And this is how you can use it.
#include "SomeSubjectImpl.h"
#include "SomeObserverImpl.h"
int main()
{
SomeSubjectImpl subject;
SomeObserverImpl observer1("observer1");
SomeObserverImpl observer2("observer2");
subject.registerObserver(&observer1);
subject.registerObserver(&observer2);
subject.doBussinessLogicAndNotify("subject_notif");
subject.unregisterObserver(&observer1);
subject.doBussinessLogicAndNotify("another_subject_notif");
subject.unregisterObserver(&observer2);
return 1;
}
Source code
You can find the entire source code for the project on my github here. There is also the Singleton implementation in this repo, presented in this article.