Understanding the Observer Pattern

Ignatius Teo
Ignatius Teo
Share

Recently I was asked to integrate third-party forum software into an existing web portal. The problem was, both applications had their own separate authentication mechanism. From a user perspective it would be ideal for users to log in to the portal without having to log in to the forum separately. And in this case, I didn’t want to modify the forum code unnecessarily since it would create a maintenance nightmare – I’d have to merge any subsequent updates and bug fixes from the vendor and be careful not to overwrite my own modifications. This is where the Observer Pattern came in handy for me. In this article I’ll show you how to implement the Observer Pattern. You’ll learn how various classes in the pattern relate to one another as subject and observers, how the subject notifies observers of a change in its state, and how to identify scenarios where it would be suitable to use the Observer Pattern in your own code.

The Observer Pattern

The Observer Pattern (also known as Publish-Subscribe Pattern) is a behavioral design pattern which defines a one-to-many relationship between objects such that, when one object changes its state, all dependent objects are notified and updated automatically. In my case, I used this pattern to link the portal’s authentication mechanism with the forum software. The act of logging into the portal also automatically triggered the user to be logged into the forum as well. An object with a one-to-many relationship with other objects who are interested in its state is called the subject or publisher. Its dependent objects are called observers or subscribers
. The observers are notified whenever the state of the subject changes and can act accordingly. The subject can have any number of dependent observers which it notifies, and any number of observers can subscribe to the subject to receive such notifications.

The Observer Class

The Observer class provides an update() method which will be called by the subject to notify it of the subject’s state change. In this example, I’ve defined the update() method as a concrete method. If you like, you can define the method here as abstract
and then provide a concrete implementation for it in your observer’s subclass.
<?php
abstract class Observer
{
    public function __construct($subject = null) {
        if (is_object($subject) && $subject instanceof Subject) {
            $subject->attach($this);
        }
    }

    public function update($subject) {
        // looks for an observer method with the state name
        if (method_exists($this, $subject->getState())) {
            call_user_func_array(array($this, $subject->getState()), array($subject));
        }
    }
}
The __construct() method accepts an instance of an observable subject and attaches itself to the subject – which I’ll get to momentarily. The update() method retrieves the subject’s current state and uses it to call a subclassed observer method with the same name as the state. So, in my particular case, I needed to make the portal’s existing Auth class an observable subject and create a concrete observer subclass to hook into the forum’s authentication code. My subclass also needed to implement methods using the subject’s states.

The Subject Class

The Subject class is also an abstract class and defines four primary methods: attach(), detach(), setState(), and notify(). For convenience, I’ve also added getState()
and getObservers() methods here.
<?php
abstract class Subject
{
    protected $observers;
    protected $state;

    public function __construct() {
        $this->observers = array();
        $this->state = null;
    }

    public function attach(Observer $observer) {
        $i = array_search($observer, $this->observers);
        if ($i === false) {
            $this->observers[] = $observer;
        }
    }

    public function detach(Observer $observer) {
        if (!empty($this->observers)) {
            $i = array_search($observer, $this->observers);
            if ($i !== false) {
                unset($this->observers[$i]);
            }
        }
    }

    public function getState() {
        return $this->state;
    }

    public function setState($state) {
        $this->state = $state;
        $this->notify();
    }

    public function notify() {
        if (!empty($this->observers)) {
            foreach ($this->observers as $observer) {
                $observer->update($this);
            }
        }
    }


    public function getObservers() {
        return $this->observers;
    }
}
The attach() method subscribes an observer to the subject so that any changes in state can be conveyed to it. The detach() method unsubscribes the observer from the subject, so that it no longer observes the subject for state changes. The setState()
method sets the subject’s current state and calls notify() to update the observers, i.e. publishes notifications to each observer. The notify() method in turn updates each of the subscribed objects by iterating through the internal list and calling each member’s update() method. The getState() and getObservers()
methods are simply helper functions to return the current subject’s state and list of observers.

Observe Closely… Bringing it Together

Now with abstract base classes for both an observer and subject, I was able to integrate the forum software into the existing web portal. I needed to make the portal’s Auth class an observable subject and set its observable state when the user logs in or out of the portal.
<?php
class Auth extends Subject
{
    function login() {
        // existing code to perform login authentication
        // ...

        // signal any observers that the user has logged in
        $this->setState("login");
    }

    function logout() {
        // existing code to perform some operation when user logs out
        // e.g. destroy session, etc...

        // signal any observers that the user has logged out
        $this->setState("logout");
    }
}
I extended the Subject class so that Auth
is observable, and then added a call to setState() in both the login() and logout() methods. To subclass the Observer, I created an Auth_ForumHook
class which was responsible for calling the forum’s API login and logout functions.
<?php
class Auth_ForumHook extends Observer
{
    function login($subject) {
        // call the forum's API functions to log the user in
        // ...
    }

    function logout($subject) {
        // call the forum's API functions to log the user out
        // ...
    }
}
Elsewhere in the code base, where the portal’s Auth class was being instantiated, I attached an Auth_ForumHook, instance to it so that the observer would be notified of any state changes in Auth.
<?php
$auth = new Auth();
// attach an observer
$auth->attach(new Auth_ForumHook());
...
That was the extent of my extra coding requirements – apart from preparing the abstract Observer and Subject classes. Any change in state triggered by Auth‘s login() and logout()
methods notified the Auth_ForumHook observer and automatically logged the user in or out of the forum. To add new observers, say, a login tracker to record when a user logs in or out of the portal, one would just have to provide a concrete Observer class and attach it to the Auth subject without the need to further modify the existing Auth object’s login()
and logout() methods.

Summary

The Observer Pattern is an appropriate design pattern to apply in any situation where you have several objects which are dependent on another object and are required to perform an action when the state of that object changes, or an object needs to notify others without knowing who they are or how many there are. In this article, I have shown you the basic Subject-Observer pattern and provided concrete examples of how you can use this behavioral pattern to easily extend the functionality of an existing class without tightly coupling any new requirements. This pattern enables you to achieve a higher level of consistency between related and dependent objects without sacrificing code re-usability. Image via JPF / Shutterstock

Frequently Asked Questions (FAQs) about the Observer Pattern

What is the main purpose of the Observer Pattern?

The Observer Pattern is a software design pattern that establishes a one-to-many dependency between objects. When one object changes state, all its dependents are notified and updated automatically. This pattern is mainly used to implement distributed event handling systems, in “event-driven” software. It promotes loose coupling and is often used in Java development.

How does the Observer Pattern work?

The Observer Pattern works by having a subject and multiple observers. The subject is an object that holds the state of interest. Observers are the dependent objects that need to be updated when the state of the subject changes. Observers subscribe to the subject and the subject notifies them when its state changes.

What are the benefits of using the Observer Pattern?

The Observer Pattern promotes loose coupling between objects. This means that objects can interact with each other without being tightly bound together. This makes the system more flexible and easier to modify or extend in the future. It also allows for a dynamic relationship between the subject and observers, as observers can be added or removed at runtime.

When should I use the Observer Pattern?

The Observer Pattern is best used when a change to one object requires changing others, and you don’t know how many objects need to be changed. It’s also useful when an object should be able to notify other objects without making assumptions about who these objects are. In other words, you should use the Observer Pattern when you want to maintain consistency between related objects without making them tightly coupled.

Can you provide a simple example of the Observer Pattern?

Sure, let’s consider a weather station (the subject) and multiple display elements (the observers). The weather station measures temperature, humidity, and pressure and updates these measurements regularly. The display elements, such as a current conditions display or a statistics display, need to show the latest measurements. They subscribe to the weather station and get updated whenever the measurements change.

What are the drawbacks of the Observer Pattern?

One of the main drawbacks of the Observer Pattern is that it can lead to complex and unexpected interactions between objects, especially if an observer responds to updates by changing the subject, leading to a chain of updates. Also, if not implemented carefully, it can lead to memory leaks as observers that are no longer needed are not properly removed from the subject’s list of observers.

How does the Observer Pattern differ from other design patterns?

The Observer Pattern is a behavioral design pattern that focuses on communication between objects. It differs from structural patterns, which deal with object composition, and creational patterns, which deal with object creation mechanisms. The Observer Pattern specifically deals with the scenario where an object, the subject, needs to automatically notify a list of dependents, the observers, about any changes to its state.

Is the Observer Pattern used in real-world applications?

Yes, the Observer Pattern is widely used in real-world applications. It’s used in graphical user interface (GUI) libraries, where it helps to update the user interface when the data changes. It’s also used in model-view-controller (MVC) architectural pattern, where it helps to update the view when the model changes.

Can the Observer Pattern be used in multithreaded applications?

Yes, the Observer Pattern can be used in multithreaded applications, but care must be taken to synchronize the access to the subject’s state and the list of observers. Without proper synchronization, race conditions can occur, leading to inconsistent results.

Are there variations of the Observer Pattern?

Yes, there are variations of the Observer Pattern. One common variation is the “push model”, where the subject sends detailed information about the change to the observers, whether they need it or not. Another variation is the “pull model”, where the subject only notifies the observers that a change has occurred, and the observers have to query the subject for the details if they need them.