Observer Design Pattern explained with simple example

Observer design pattern is one of the most famous and widely used design pattern in software industry. In this design pattern basically multiple objects which are called as observers, registers themselves to an object called as subject for getting automatic notifications, whenever any state changes occurs in subject. This is mainly used for event handling modules.

Before going ahead have a look at Design pattern simplified version.

Generic model class diagram of Observer design pattern is as shown in image.

Observer design pattern class diagram

In this design pattern an observer registers itself to a subject for receiving automatic notifications of any state change. Subject maintains a list of all the registered observers. Whenever any state change occurs in Subject it notifies all the observers to handle Subject’s state change event.

Observer design pattern Code Example:

Let’s take an example from real world. People wants to get notified about their loved one’s birthday so that they can wish them Happy birthday. Here, we are trying to solve this problem using Observer design pattern.

#include <iostream>
#include <unistd.h>
#include <vector>

using namespace std;

class Person;
class Observer
{
	public:
		Observer ()
		{}
		virtual void event_handler (Person *p) = 0;
		virtual ~Observer ()
		{}
};

class Observable
{
	public:
		void registerObserver (Observer *obj);
		void unregisterObserver (Observer *obj);
		void notify ();
};

class Person: public Observable
{
	private:
		int age;
		vector <Observer *> observers;

	public:
		Person (int a): age (a)
		{}

		int getAge ()
		{
			return age;
		}

		void setAge (int a)
		{
			if (age != a)
			{
				age = a;
				notify ();
			}
		}

		void registerObserver (Observer *obj)
		{
			observers.push_back (obj);
		}

		void unregisterObserver (Observer *obj)
		{
			if (obj)
			{
				vector <Observer *>::iterator itr = observers.begin ();
				while (itr != observers.end ())
				{
					if (obj == *itr)
					{
						observers.erase (itr);
						break;
					}
					itr++;
				}
			}
		}

		void notify ()
		{
			for (auto obj: observers)
				obj->event_handler (this);
		}
};

class PersonObserver: public Observer
{
	public:
		string name;
		PersonObserver (string n): Observer ()
		{
			name = n;
		}
		virtual void event_handler (Person *p) override
		{
			cout << name <<" wishes happy birthday for birthday number: "<< p->getAge () << endl;
		}
};

int main ()
{
	Person p (21);
	PersonObserver po1 ("John");
	PersonObserver po2 ("Mary");
	PersonObserver po3 ("Dean");

	p.registerObserver (&po1);
	p.registerObserver (&po2);
	p.registerObserver (&po3);

	p.setAge (22);

	p.unregisterObserver (&po3);

	p.setAge (23);
}

Output of above example:

John wishes happy birthday for birthday number: 22
Mary wishes happy birthday for birthday number: 22
Dean wishes happy birthday for birthday number: 22
John wishes happy birthday for birthday number: 23
Mary wishes happy birthday for birthday number: 23

Advantages:

  • Loosely coupled design as both classes are linked through well defined APIs.
  • Efficient method to notify multiple observers of any event.

Disadvantages:

  • Thread safety can be a big concern in case of multi threaded system where unregister and register function calls needs to be protected with locks as both functions modifies the observers vector.

Leave a Reply

Your email address will not be published. Required fields are marked *