Observer Design Pattern

The Observer Pattern

According to the GoF book the Observer pattern “defines a one to many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically”. So this pattern is all about objects (the observers, or listeners) that are interested about the state of another object (the subject, or observable), and want to know when and how this state changes, so they can update themselves accordingly and reflect the changes. One way to achieve this would be to continuously poll the observed object for a change, for example in a loop, so every iteration each object would ask the observed object if its state has changed (an inefficient way to say the least). The Observer pattern let us decouple the observers from the subject, in the sense that an observed object doesn’t know who or how many are its observers. Observers register with the subject and when the subject changes state it notifies all its registered observers. Let’s analyze the class diagram for this pattern:

The abstract subject’s interface exposes methods to add, remove and notify observers: the implementation for these methods can be placed into the Subject class or into the derived ConcreteSubject classes (making Subject an abstract interface). The Observer interface is simple: it has only one abstract method called Update(). The method’s parameter list depends on the notification model we choose for the application: in the pull model, the subject notifies the observers and these in turn get informations about what changed by querying the concrete subject (they pull informations from the subject); this approach encreases decoupling, since the subject doesn’t need to know anything about the observers, and about what they need to know when the state changes (adding new observers is easy). So the parameter list of the Update() method can be left empty (if the concrete observers have a reference to the observed subject), or the subject can pass itself to the Update() method. With the push model instead, it’s the subject that provides (pushes) informations about what has changed to the observers: the parameters in the Update() method’s parameter list enclose the informations about the change. On one hand, the concrete observer doesn’t need a reference to a concrete subject to retrieve its state (they still need it to attach and remove themselves dynamically to the subject), but this approach increases the coupling between the subject and its observers, since the subject needs to know what informations the observers need, and adding different observers may be more difficult:

This is the Observer pattern in a nutshell. It is simple but extremely powerful. Let’s see an example of the pattern at work.

An example: actors and game systems

In game development an actor is an entity in our game: it may have a position, an orientation, a mesh and other properties. A player is an actor, and during the game it can interact with other actors, change its state and perform other tasks that we put into the game logic. Changes in the player’s state must be communicated to various systems in the game or to other actors so that they can reflect those changes and react to those changes (change their state, or do something else). Think about a GUI that has a health bar that displays the remaining life of the player. The player’s state contains a variable called health: it decreases every time the player is hit, and increases if our hero bumps into some sort of medipack or life-extending power up. Every time the player’s health changes, the health bar must update its state to reflect the player’s. Maybe we want our audio system to play an “ouch” sound every time the player’s hit, or a grim tune on our player’s unlucky demise? The observer pattern makes coding these dependencies easy. Let’s have a look to a naive implementation for the subject base class:

// Observer.h

#define SIZE 20

class Observer;   // forward declaration of observer interface

class Subject
{
public:
	virtual ~Subject() {}
	void AddObserver(Observer *observer);
	void RemoveObserver(Observer *observer);
	void Notify();
protected:
	Subject() : m_count(0) { for (int i = 0; i < SIZE; i++) m_observers[i] = nullptr; }   // can't instantiate directly (abstract interface)
private:
	Observer *m_observers[SIZE];   // a simple array of pointers
	int m_count;
};

// Observer.cpp

void Subject::AddObserver(Observer *observer)
{
	for (int i = 0; i < m_count; i++)
		if (m_observers[i] == observer)
			return;
		
	if (m_count < SIZE)
		m_observers[m_count++] = observer;
}

void Subject::RemoveObserver(Observer *observer)
{
	for (int i = 0; i < m_count; i++)
		if (m_observers[i] == observer)
		{
			for (int j = i; j < m_count - 1; j++)
				m_observers[j] = m_observers[j + 1]; 
			m_count--;
			break;
		}
}

void Subject::Notify()
{
	for (int i = 0; i < m_count; i++)
		m_observers[i]->Update();
}

In this example I put the implementation of the observer related methods into the Subject abstract base class. The subject keeps track of registered observers in a simple static array. Our concrete subject is the Player class:

// Player.h

#include "Observer.h"

class Player : public Subject
{
public:
	Player(int life) : m_initial_life(life), m_health(m_initial_life) {}
	void Hit();
	void PickUpHealth();	
	int GetLife() const { return m_initial_life; }      // get total life
	int GetHealth() const { return m_health; }          // get current health
private:
	const int m_initial_life;
	int m_health;
};

// Player.cpp

void Player::Hit()
{ 
	if (m_health > 0) 
		m_health--; 
	
	// state changing operation - notify observers
	Notify();
}

void Player::PickUpHealth()
{
	if (m_health < m_initial_life) 
		m_health++;
	
	// state changing operation - notify observers
	Notify();
}

A player is spawned with an initial life (each player has its own initial value), and has getter methods to retrieve its current health state, and a Hit() and PickUpHealth() methods, that decrease and increase its health respectively. These are state changing operations so every time they are called all observers are notified. Let’s have a look at the observer’s inteface:

// Observer.h

class Observer
{
public:
	virtual ~Observer() {}
	virtual void Update() = 0;
protected:
	Observer() {}
};

As we saw earlier, the interface is really simple, it just declares an abstract method Update(). In this example I use a pull model for notifications, so the observer will have to do a little more work to retrieve informations about the change in the subject using the Player‘s getter methods. A reference to the concrete subject is set upon the concrete observer construction. The health bar is our first observer: it observes the player for change and reflects the changes by displaying the player’s remaining health. Since we don’t have time here to setup a rendering system we’ll show the current health by printing the value to the console:

// GUI.h

#include "Observer.h"

class GUISystem : public Observer
{
public:
	GUISystem(Player *subject);   // reference to subject is set upon construction
	void Update() override;
private:
	void DisplayHealthBar() const;
	Player *m_subject;
	float m_health;
};

// GUI.cpp

#include <iostream>

GUISystem::GUISystem(Player *subject) : m_subject(subject), m_health(-1)
{
        // register observer
	m_subject->AddObserver(this);
	
	// first update to display initial health bar
	Update();
}

void GUISystem::Update()
{
        // get subject's state and update observer's state
	m_health = (float)m_subject->GetHealth() / (float)m_subject->GetLife();
	
        // display updated state
	DisplayHealthBar();
}

void GUISystem::DisplayHealthBar() const
{
	std::cout << "health bar " << m_health * 100.0f << "% full" << std::endl;
}

The constructor sets the concrete subject instance and register this observer with it. The Update() implementation gets the current health from the player, and displays (prints) it on screen. Another observer is the sound system, which is in charge of playing sounds as the player’s state changes:

// Sound.h

#include "Observer.h"

class SoundSystem : public Observer
{
public:
	SoundSystem(Player *subject);
	void Update() override;
	void MuteSound();
	void EnableSound();
private:
	enum class Sound { HIT, DEAD, POWER_UP, };
	void PlaySound(Sound sound) const;
	Player *m_subject;
	int m_health;
};

// Sound.cpp

SoundSystem::SoundSystem(Player *subject) : m_subject(subject)
{
	m_subject->AddObserver(this);
	
	// get player's initial health
	m_health = m_subject->GetHealth();
}

void SoundSystem::Update() 
{
	int current_health = m_subject->GetHealth();
	
	if (current_health < m_health && current_health)
		PlaySound(Sound::HIT);
	else if (current_health > m_health)
		PlaySound(Sound::POWER_UP);
	else if (current_health == 0)
		PlaySound(Sound::DEAD);
	
	m_health = current_health;	
}

// detach from subject
void SoundSystem::MuteSound()
{
	// de-register observer
	m_subject->RemoveObserver(this);
	
	std::cout << "sound off" << std::endl << std::endl;
}

// attach to subject
void SoundSystem::EnableSound()
{
	// re-register observer
	m_subject->AddObserver(this);
	
	// get player's current health
	m_health = m_subject->GetHealth();
	
	std::cout << "sound on" << std::endl << std::endl;
}

void SoundSystem::PlaySound(Sound sound) const
{
	switch (sound)
	{
		case Sound::HIT:
			std::cout << "playing \"ouch\" sound..." << std::endl;
			break;
		case Sound::DEAD:
			std::cout << "playing funeral march sound..." << std::endl;
			break;
		case Sound::POWER_UP:
			std::cout << "playing power up sound..." << std::endl;
			break;
	}
}

The implementation is a little more complex because it includes methods to unregister and register again the observer dynamically at runtime (this is another reason to keep a reference to the concrete subject in the concrete observer object). The sound system plays a different sound based on the variation of the health of the player. Again, we don’t have a working sound API so we will make do with printing a message to the console. When the player’s health bar is empty, a death march tune is played and the game is over. How can we tell our game logic that we need to display a game over image and exit the game? But by adding another observer of course:

// Game.h

#include "Observer.h"

class GameSystem : public Observer
{
public:
	GameSystem(Player *subject);
	void Update() override;
	bool IsOver() { return m_game_over; }
private:
	Player *m_subject;
	bool m_game_over;
};

// Game.cpp

GameSystem::GameSystem(Player *subject) : m_subject(subject), m_game_over(false)
{
	m_subject->AddObserver(this);
}

void GameSystem::Update()
{
	if (m_subject->GetHealth() == 0)
	{
		m_game_over = true;
		
		std::cout << "game over!" << std::endl;
	}
}

The GameSystem class is another observer and represents that part of our game logic that deals with the state of the game. When the player’s life is depleted, this informs the game system, which sets its m_game_over flag to true. The flag is polled by the game main loop:

// Game.cpp

#include "Observer.h"
#include <iostream>

int main(int argc, char *argv[])
{
	// create subject/observable
	Player *player_one = new Player(4);  
	
	// create and register observers
	GUISystem guiSystem(player_one);
	SoundSystem soundSystem(player_one);
	GameSystem game(player_one);
	
	do 
	{
		char c;
		
		std::cout << "press \"h\" to hit player, \"p\" to pick up health power_up (press \"m\"/\"e\" to mute/enable sound): ";
		std::cin >> c;
		std::cout << std::endl;
	
		if (c == 'h')
		{
			player_one->Hit();
			std::cout << std::endl;
		}
		else if (c == 'p')
		{
			player_one->PickUpHealth();
			std::cout << std::endl;
		}
		else if (c == 'm')
			soundSystem.MuteSound();
		else if (c == 'e')
			soundSystem.EnableSound();
		else
			std::cout << "unknow command - ";		
	} while (std::cin && !game.IsOver());
	
	return 0;
}

The game loop asks the user to interact with the player by hitting him or giving him health power ups. If we want to mute the sound (disable the console messages in our case) we can unregister the SoundSystem dynamically, or re-register it back if we change our mind. When the player’s dead, a grim tune is output from the speakers (with a little imagination) and “game over” is displayed and the game exits. An example output might be:

health bar 100% full
press "h" to hit player, "p" to pick up health power_up (press "m"/"e" to mute/enable sound): h

health bar 75% full
playing "ouch" sound...

press "h" to hit player, "p" to pick up health power_up (press "m"/"e" to mute/enable sound): h

health bar 50% full
playing "ouch" sound...

press "h" to hit player, "p" to pick up health power_up (press "m"/"e" to mute/enable sound): h

health bar 25% full
playing "ouch" sound...

press "h" to hit player, "p" to pick up health power_up (press "m"/"e" to mute/enable sound): p

health bar 50% full
playing power up sound...

press "h" to hit player, "p" to pick up health power_up (press "m"/"e" to mute/enable sound): h

health bar 25% full
playing "ouch" sound...

press "h" to hit player, "p" to pick up health power_up (press "m"/"e" to mute/enable sound): m

sound off

press "h" to hit player, "p" to pick up health power_up (press "m"/"e" to mute/enable sound): p

health bar 50% full

press "h" to hit player, "p" to pick up health power_up (press "m"/"e" to mute/enable sound): p

health bar 75% full

press "h" to hit player, "p" to pick up health power_up (press "m"/"e" to mute/enable sound): e

sound on

press "h" to hit player, "p" to pick up health power_up (press "m"/"e" to mute/enable sound): h

health bar 50% full
playing "ouch" sound...

press "h" to hit player, "p" to pick up health power_up (press "m"/"e" to mute/enable sound): h

health bar 25% full
playing "ouch" sound...

press "h" to hit player, "p" to pick up health power_up (press "m"/"e" to mute/enable sound): h

health bar 0% full
game over!
playing funeral march sound...

Leave a Reply

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