6 July 2012

A simple implementation of Observer (Listener) pattern

I was just writing an example and I thought it might be interesting for some people. So I decided to post it here. What the Observer pattern resembles, I think most of you know. The same pattern with probable little modifications is known as Listener, Signal/Slot, Event Handling, etc. The idea is that one object somehow lets another object or objects know about some change (mouse click, text edited, some member data updated, a row deleted from the table in database, etc.). This action is called notification, so an object notifies another object about the change. The latter - another object - is called a listener, or a handler, or an observer, etc.

The main point to consider is that the notifier should somehow keep the references of the listeners, otherwise the latter will never know about the change. But how is it possible? For first, let's admit that the notifier does not care much of whoever is observing it. It has its own functionality, and it acts as expected, it is out of its responsibilities to find the listeners and ask whether they are interested in some specific changes. So the listeners themselves know what they want to listen to, what they need to observe, and it is their own responsibility to register/subscribe for the updates from interesting objects. Thus, the notifier should only provide a mechanism for listeners to register for notifications. As soon as the listener is registered, the notifier can easily let know it about any interesting change. Ok, good for now.

But, what does it mean "to let know"? It means that each listener registering for notifications from the notifier should itself provide a method for being notified. For this purpose all the listeners should have a common interface (IListener in the sample code). Now seems everything can be properly done. Here is the schema again:

1. all the listeners are derived (can provide a common interface in another ways also) from an abstract base IListener
2. the notifier provides methods for listeners to register/unregister
3. the notifier keeps a collection of registered listeners
4. each time a change occurs, the notifier lets all the registered listeners know by means of their own method provided by the common interface (HandleNotification() in this example)
5. HandleNotification() is a member function of the listener so the latter can react to the notification as needed.

Here is a code sample for the above solution:

#include <iostream>
#include <set>
#include <algorithm>

class IListener
{
public:
       virtual void HandleNotification() = 0;
};

class Notifier
{
protected:
       virtual void Notify()
       {
              std::for_each(m_listeners.begin(), m_listeners.end(), [&](IListener *l) {l->HandleNotification(); });
       }

public:
       virtual void RegisterListener(IListener *l)
       {
              m_listeners.insert(l);
       }

       virtual void UnregisterListener(IListener *l)
       {
              std::set<IListener *>::const_iterator iter = m_listeners.find(l);
              if (iter != m_listeners.end())
              {
                     m_listeners.erase(iter);
              } else {
                     // handle the case
                     std::cout << "Could not unregister the specified listener object as it is not registered." << std::endl;
              }
       }

private:
       std::set<IListener*> m_listeners;
};


For a more practical example of using the classes above, see the code below:

class Button : public Notifier
{
public:
       void Press()
       {
              Notify();
       }
};

class TextBox : public IListener
{
public:
       TextBox() : num(0)
       { }
private:
       int num;
      
private:
       virtual void HandleNotification()
       {
              std::cout << "The button has been pressed " << ++num << " times" << std::endl;
       }

};

int main()
{
       Button b1;
       TextBox t1;
       b1.RegisterListener(&t1);
       b1.Press();

       return 0;
}

3 comments: