Thursday, 1 October 2015

GoF: Observer Pattern (aka Publish-Subscribe)

Description

The Observer Pattern is one the fundamental design patterns described by the Gang Of Four (GoF) in the volume “Design patterns – Elements of reusable object-oriented software”.
It is a behavioural pattern used when there is a “one to many” dependency between different objects: when the observed object (called Subject in the pattern) changes its state, all objects depending on it (called Observers) must be notified and updated.
This kind of interaction is also known as publish-subscribe.


UML diagram

Implementation

The code of the following example can be found here: Design Patterns in Java Github Repository.

Subject


public class Subject {
 
   private List observers = new ArrayList();
   private int state;

   public int getState() {
      return state;
   }

   public void setState(int state) {
       this.state = state;
       notifyAllObservers();
   }
 
   public void attach(Observer observer){
      observers.add(observer);  
   }

   public void notifyAllObservers(){
      for (Observer observer : observers) {
         observer.update();
      }
   }  
}

Subject is the object from which other objects depend (Observers). The class Subject keeps an internal list of Observers (see below) that can be added with the public method “attach(Observer observer)” (the class could also implement a “detach” method for removing Observers that are not used anymore).
The variable “state” represents the internal state of this object (in our simplified example it’s an integer), with public setter and getter methods.
When the state of the Subject varies, through the setState method, we have to iterate over the list of Observers and notify the change to each of them. The notification is sent to all attached Observers: it’s up to the Observer to handle or ignore the notification.
As you can see from the example above it’s very important that notifyAllObservers() is called only after the state of the Subject is updated, and not before (the Subject state must be self-consistent), because all Observers will be updated as a result of the notification. This is especially true if you need to subclass the Subject class. A possible solution is to define a template method with abstract operations that the subclasses can override, and call notifyAllObservers() as the last instruction of the method.
Notice that the class Subject keeps strong references to the attached Observers. This can cause memory leaks if some Observers are not used anymore: to fix this problem you can create a Subject class keeping weak references to the Observers, instead of strong ones.

Abstract Observer

public abstract class Observer {
   protected Subject subject;
   public abstract void update();
}

Observer is an abstract class that must be extended. It keeps a reference to the Subject (to be able to be connected to it through the “attach” method and to get/set its state through the public setter/getter methods); it also exposes an abstract “update” method that gets invoked when the internal state of the Subject varies (this is the way the Subject communicates its changes to the Observers).

Concrete Observer

public class BinaryObserver extends Observer{
   private Subject subject;

   public BinaryObserver(Subject subject){
      this.subject = subject;
      this.subject.attach(this);
   }

   @Override
   public void update() {
      System.out.println( "Binary String: " + Integer.toBinaryString( subject.getState() ) ); 
   }
}

There are two things to notice here: 

  1. the ConcreteObserver “BinaryObserver” (which prints the number representing the state of the Subject in binary notation) keeps a reference to the Subject: this reference is used to connect this ConcreteObserver to the Subject (“attach” method of the Subject) and to retrieve the Subject’s state through the “getState” method; 
  2. the class exposes an “update” method used to receive the notification sent by the Subject when its state varies.
The ConcreteObserver can also directly edit the state of the Subject via the “setState” method. In this case the ConcreteObserver that made the change gets also notified by the Subject (the method “nofityAllObservers” iterates over all attached Observers) and, after receiving the notification, it may possibly change its internal state.

public class OctalObserver extends Observer{
   private Subject subject;

   public OctalObserver(Subject subject){
      this.subject = subject;
      this.subject.attach(this);
   }

   @Override
   public void update() {
     System.out.println( "Octal String: " + Integer.toOctalString( subject.getState() ) ); 
   }
}


This is another ConcreteObserver, printing the variable “state” of the Subject in octal notation.

Client

public class ObserverPatternDemo {
   public static void main(String[] args) {
      Subject subject = new Subject();

      new OctalObserver(subject);
      new BinaryObserver(subject);

      System.out.println("First state change: 15"); 
      subject.setState(15);
      System.out.println("Second state change: 10"); 
      subject.setState(10);
   }
}

 
Sequence

Insights

Advantages

The Observer pattern enables you to decouple the Subject and the Observers, making them reusable and modifiable independently; it also enables you to add or remove Observers according to your specific needs. Infact the Subject only knows to have some Observers attached to it and which method it has to invoke to notify its state changes (interface for notifications), but it doesn’t know more: what kind of classes they are, what they do, etc. (the coupling is abstract and minimal). If the Observers were tightly coupled to the Subject this would not be possible.
Being weakly coupled, the Subject and the Observers could belong to different layers of abstraction in a system: for example the Subject could lie on a lower level and the Observers in a higher level, thus preserving the layering of the system.


Observers depending on different Subjects

A very easy way to handle this situation consists of passing the Subject itself as a parameter in the “update” method, thus allowing the Observer to understand from which Subject the notification is coming.
If we have different interdependent Subjects and an Observer attached to them, the Observer pattern, as previously described, could be inefficient, because the Observer could receive multiple sequential notifications from the different Subjects as their states change. A better solution would be to have a single notification issued when all Subjects have changed. Solution: ChangeManager.

Notify called from the Client

With the standard Observer pattern, where the Subject invokes “update” on each Observer to notify its state changes (see the method “setState”), inefficiencies may arise when there are multiple sequential modifications on the Subject: infact each modification is notified to each Observer separetely.
In a different and better implementation the Client changes the Subject’s state and is responsible for notifying all Observer only after the different sequential modifications have been made. However if the Client forgets to issue the notifications errors may arise.

Update” method with or without parameters (push or pull model)


  • Without parameters (see the example above): the Subject simply informs all Observers that a change has occurred, but it’s the Observer’s responsibility to understand exactly what happened (pull method); this implementation decouples Subject and Observers making them easily reusable, but the notification mechanism may become inefficient.
  • With parameters: the Subject gives the Observers detailed information about the change that has occurred (push method): the notification management is more efficient, but Subject and Observers become more tightly coupled and therefore less easily reusable.

Specifying modifications of interest

Let’s suppose that an Observer is interested only in changes of the Subject of some kind, and not all. A very easy solution is to specify in the “attach” method of the Subject the type of modification of interest:
public void attach(Observer observer, Interests interest), where interest is the type of modification of interest
We also have to change the “update” method of the AbstractObserver to specify what kind of modification occurred:
public abstract voi update(Observer observer, Interests interest)

Applications

The Observer pattern is often used in the MVC – Model View Controller models and in almost all user interface toolkits. The Model (the data) is the Subject, the View (the UI) is the Observer: when data changes the UI must be updated accordingly. This way data and presentations can be reused independently.
Consider an Excel spreadsheet with some data and a pie chart and a bar chart. When data changes the pie chart and bar chart must be updated (the charts play here the role of the Observers while the data in the spreadsheet is the Subject).

No comments:

Post a Comment