Observer Design Pattern Example

Observer Design Pattern: Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. -- By THE GOF BOOK

When to use? Observer design pattern is useful when we are interested in the state of an object and want to get notified whenever there is any change in the state of the object. In observer pattern, the object that watch the state of another object is called Observer and the object that is being watched is called Subject.

For example: In GUI programming, event-driven is an important concept. Such as, when you do programming in java UI /swing, you may put a button on the stage, then you’ll add a callback function to the button. The function will be called when there is some action play on the button, such as click. This is almost the same with the observer pattern.

Lets make it clear. Firstly, we care the state of the button; we want know when the button was clicked. Secondly, when the button state was changed, we want to do something, that’s the callback function. Actually, you can consider as the function cares the state of the button. When the button state change, the function will do something. So here the button is observable, and the function maybe the observer.

Keep in mind, An observable object can have one or more observers. An observer may be any object that implements interface Observer. After an observable instance changes, an application calling the Observable's notifyObservers method causes all of its observers to be notified of the change by a call to their update method.

Note that this notification mechanism is has nothing to do with threads and is completely separate from the wait and notify mechanism of class Object.

Lets understand Observer pattern with an example, suppose we have a GUI application in which we have to display some text message on screen dynamically, and to maintain the history of message we need to save this massage in database or in a file, and for debug we need to log all messages as soon as a new message is available.

To do this we need a simple bean class MessageDataVO which is used to set the message once a new message is available. Once the message is set this dataVO should notify all other components to update the UI, log the message and save the message. The class diagram for this mechanism can be shown as

Implementing Observer pattern: To implement the above solution using Observer, we need to create the Observer interface, Observable class and other required classes as following.

public interface Observer {
         void update(Observable obj, Object data);
}

public class Observable {
private boolean changed = false;
private ArrayList<Observer> observers;

public Observable() {
observers = new ArrayList<Observer>();
}

public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!observers.contains(o)) {
observers.add(o);
}
}

public void notifyObservers() {
notifyObservers(null);
}

public void notifyObservers(Object data) {
synchronized (this) {
if (!changed)
return;
clearChanged();
}
for (Observer observer : observers) {
observer.update(this, data);
}
}

public synchronized void deleteObservers() {
observers.clear();
}

protected synchronized void setChanged() {
changed = true;
}

protected synchronized void clearChanged() {
changed = false;
}

public synchronized boolean hasChanged() {
return changed;
}

public synchronized int countObservers() {
return observers.size();
}
}

Using this Observable class we will create the MessageDataVO class as following

public class MessageDataVO extends Observable {
private String message;

public void setMessage(String message) {
this.message = message;
setChanged();
notifyObservers(message);
}

public String getMessage() {
return message;
}
}

This MessageDataVO Bean class will be used as subject and we will observe this when its state is changed or when the message is set to this bean then it will notify the other observers explained as following.
Now we will create the MessageUIObserver, MessagSaveObserver and MessageLogObserver which are interested in getting the the message once it is available to dataVO bean.

public class MessageUIObserver implements Observer {
@Override
public void update(Observable obs, Object data) {
System.out.println("Message to show: " + data);
}
}

public class MessagSaveObserver implements Observer {
@Override
public void update(Observable obs, Object data) {
System.out.println("Message to save: " + data);
}
}

public class MessageLogObserver implements Observer {
@Override
public void update(Observable obs, Object data) {
System.out.println("Message to log: " + data);
}
}

Now to test this implementation we need to create a ObserverTestClient as following

public class ObserverTestClient {
public static void main(String[] args) {
MessageDataVO dataVO = new MessageDataVO();
//create Observers for above dataVo object
dataVO.addObserver(new MessageUIObserver());
dataVO.addObserver(new MessagSaveObserver());
dataVO.addObserver(new MessageLogObserver());
//now all observers are registered for receiving message updates.
//now we will change the message in dataVO
dataVO.setMessage("This is Observer test message");
//all Observers will receive this message via notification
}
}

Here all the Observers are registered to MessageDataVO and once the message is posted to this dataVO all the registered observers will be notifyied to process the posted message. The output of this implementation is printed as following

Output:

Message to show: This is Observer test message
Message to save: This is Observer test message
Message to log: This is Observer test message

Advantage of Observer:
Main advantage is loose coupling between objects called observer and observable. The subject (dataVO  bean in our case) only know the list of observers it don’t care about how they have their implementation. All the observers are notified by subject in a single event call as Broadcast communication.

Disadvantage of Observer: 
The disadvantage is that, sometime if any problem occurs, debugging becomes very difficult because flow of control is implicitly between observers and observable we can predict that now observer is going to fire and if there is chain between observers then debugging become more complex.
Another issue is Memory management because subject will hold all the reference of all the observers if we do not unregister the observer objects it can create the memory issue.

3 comments :

  1. Direct to the point example, it was helpful.
    Thanks.

    ReplyDelete
  2. very clear-cut and simple explanation of a complex concept...

    ReplyDelete