Saturday, March 13, 2010

Observer design pattern (Publisher-Subscriber)

The use of observer design pattern is ubiquitous in Java. We just have to consider Swing classes, GUI frameworks, RMI, JavaBeans etc to note its pervasive nature. Whenever a component registers for an action event, it is using the Observer pattern. The straightforward principle is that whenever multiple parties (observers) are interested in the changes of state of an object (subject/observable) then they subscribe for the notification of changes. The observable object monitors its state and on observing relevant change in its state, it notifies all the interested parties through a common update() interface. The subject provides methods for the subscription and un-subscription. Obviously, the subject can either push all the changes to the observers or allow the observers to pull the changes on notification through provides API. The key is that the subject and the observers are loosely coupled, as the subject has no knowledge of the observers.

Our sample program clearly illustrate the principle.

package raj;

import java.util.ArrayList;
import java.util.List;

public class TestObserver {

public static void main(String[] arg) {
// create the subject we are interested in subscribing
Subject subject = new Subject();
// create the subscriber for the subject
Bar interestedObserver = new Bar();
// add subscribers
subject.addObserver(new Foo());
subject.addObserver(new Bar());
subject.addObserver(interestedObserver);
// subject state changes; the observers notified
subject.stateChange();
// unsubscribe
subject.removeObserver(interestedObserver);
// check the unsubscribed party doesn't get further notification
subject.stateChange();
}
}

class Subject {

List observerList = new ArrayList();

public void addObserver(Observer o) {
observerList.add(o);
}

public void removeObserver(Observer o) {
int index = observerList.indexOf(o);
if (index >= 0) {
observerList.remove(index);
}
}

public void notifyObservers() {
for (Observer o : observerList) {
o.update("change");
}
}

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

interface Observer {

public void update(String message);
}

class Foo implements Observer {

public void update(String message) {
System.out.println("Foo: " + message);
}
}

class Bar implements Observer {

public void update(String message) {
System.out.println("Bar: " + message);
}
}

We have opted for push technique in our example but it could be more appropriate to use pull as it allows the observer more flexibility in responding to the change.

Also it is worth noticing that Java provides its own implementation of Observer and Observable in the java.util package so the above sample could have been codes as:

import java.util.Observable;
import java.util.Observer;

public class JavaObserver extends Observable {
private String message;

public String getMessage() {
return message;
}

public void stateChange(String message) {
this.message = message;
setChanged(); // signifies state change
notifyObservers(message);
// the line below is for 'pull' as we pass the object so getMessage() can be invoked
// notifyObservers(this);
}

public static void main(String[] args) {
JavaObserver subject = new JavaObserver();
Baz bar = new Baz();
Baz baz = new Baz();
Baz foo = new Baz();
subject.addObserver(foo);
subject.addObserver(bar);
subject.addObserver(baz);
subject.stateChange("Change!");
}
}

class Baz implements Observer {
public void update(Observable o, Object message) {
System.out.println("Baz: " + message); // push
// the line below is for 'pull'
// System.out.println("Baz: " + ((JavaObserver) message).getMessage());

}
}

The key restriction of the Java built-in functionality is that we have to extend the Observable class so there is no way extending JavaObserver from any other class.







No comments:

Post a Comment