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 (making it an abstract class), or into the derived ConcreteSubject classes (making Subject an 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 get information about what changed by querying the concrete subject (that’s why they keep a reference to a concrete 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 pushes the information 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 other stuff 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 perform an action). 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. And what if we want our sound system to play an “ouch” sound every time the player’s hit, or a grim sound on our player’s unlucky demise? The observer pattern makes coding these dependencies a breeze. Let’s have a look to the subject implementation:

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:

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:

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 from 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:

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:

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 around a reference to the concrete subject). 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 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:

The GameSystem class is another observer that 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:

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 text related to the sound system) 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:

Leave a Reply

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