Saturday, 7 July 2012

Observer Pattern

The observer pattern allows objects to communicate and exchange information using notifications. An observer object registers with and receives notification from a subject object. Any number of observers can register with a subject and when an event occurs a notification is sent to each of the registered observers. The observer pattern allows loose coupling between the object triggering the event and the objects that respond to the event. The diagrams below should help explain this a little better.

Figure 1.0 shows 2 observers registering with a subject.

Figure 1.1 shows the subject sending notifications to all 
registered observers when an event occurs.

Figure 1.2 shows one of the observers deregistering the subject. This 
object is no longer observing the subject and will not receive any further notifications. 

Figure 1.3 shows the subject sending a notification to the single registered observer.

Creating the Subject interface
We’ll start off by creating the Subject interface which should be implemented by any object that wants to send event notifications to other objects. The Subject interface is defined as follows.
public interface Subject
{
     public void registerObserver(Observer observer);
     public void unregisterObserver(Observer observer);
     public void notifyObservers(String event);
}
The 3 method signatures above are pretty intuitive - they provide a means of adding a new Observer, removing an existing Observer and sending a notification to all registered Observers. 

Creating the Observer interface
The Observer interface should be implemented by any object that wants to receive notification of events. The observer interface is defined as follows.
public interface Observer
{
     public void update(String event_p);
}
The update method is invoked (on the implementation class) by the Subject to notify the Observer that an event has occurred.

Creating a concrete Subject
Now we’re going to create a class called EventTrigger that implements the Subject interface.  I’ll explain each part of this class below.
public class EventTrigger implements Subject
{
     private List observers_i;

     public EventTrigger()
     {
          observers_i = new ArrayList();
     }

     @Override
     public void registerObserver(Observer observer_p)
     {
          observers_i.add(observer_p);
     }

     @Override
     public void unregisterObserver(Observer observer_p)
     {
          observers_i.remove(observer_p);
     }

     @Override
     public void notifyObservers(String event_p)
     {
          for(Observer observer: observers_i)
          {
              observer.update(event_p);
          }
     }
}
Line 3 - Defines a list that will hold all Observers registered with this object. New Observers registered with Event Trigger it will be added to this list.

Line 5 to 8 - The class constructor instantiates a new ArrayList that will be used to hold registered Observers.

Line 11 to 14 – Adds new Observer objects to EventTrigger.  The method parameter is added to our list of Observers and as a result will receive notification of events fired by EventTrigger.

Line 17 to 20 - Removes an existing Observer from EventTrigger.  The parameter is removed from the list of Observers and as a result will no longer receive notification of events fired by EventTrigger.

Line 23 to 29 – Notifies all Observers by iterating over each registered object and invoking its update method.

Creating concrete Observers
Next we’ll create the Observers that will register with the EventTrigger above. We’ll have 2 observers, EventHandler1 and EventHandler2 which must both implement the update method. Both classes are defined below. 
public class EventHandler1 implements Observer
{
     public EventHandler1()
     {

     }

     @Override
     public void update(String event_p)
     {
          System.out.println("Event handler 1 received event: " + event_p);
     }
}

public class EventHandler2 implements Observer
{
     public EventHandler1()
     {

     }

     @Override
     public void update(String event_p)
     {
         System.out.println("Event handler 1 received event: " + event_p);
     }
}
You’ll notice that both EventHandlers are pretty much identical and simply log the event triggered by
the Subject they’re registered with. I’ve created 2 events handlers so that I can show multiple observers registering with a subject.
Note: We could have replaced both the Observer and Subject interfaces with abstract classes. Interfaces 
however give us greater flexibility as the implementing class is free to implement multiple interfaces but extend only one class. By implementing interfaces in this example we are leaving the implementation classes free to participate in a different inheritance hierarchy.

Testing the Observer pattern
We’re now ready to put the observer pattern to the test with a simple test harness.
public class TestObserverPattern
{
     public static void main (String[] args_p)
     {
          Observable eventTrigger = new EventTrigger();
          eventTrigger.registerObserver(new EventHandler1());
          eventTrigger.registerObserver(new EventHandler2());

          eventTrigger.notifyObservers("This is a test event!!");
     }
}
Line 5 - Creates a new EventTrigger (subject) object - this is the Subject that Observers will register with for notifications.

Line 6 and 7 - Creates and registers a new EventHandler (observer) with the EventTrigger.

Line 9 - Sends a notification to each of the registered EventHandlers.

Running the test harness prints the following to the console. I’ve attached the code to this article so that you can download and run it yourself.

Event handler 1 received event: This is a test event!!
Event handler 2 received event: This is a test event!!

Summary
The observer pattern allows objects to communicate using a notification mechanism while remaining loosely coupled. This results in components that are easier to debug and maintain. You should consider the observer pattern for situations where you have objects triggering events that you want other objects to react to.

No comments:

Post a Comment