Thursday, 26 May 2011

Implementing the JDK's Observer Pattern

Yesterday’s blog talked about the ActionListener event handler's implementation of the Observer pattern that is woven throughout Java’s Swing classes. The JDK duplicates this pattern in an implementation with classes called Observer and Observable.

The example below demonstrates an implementation of Observer and Observable in the form of a simple news server and client that provides the same functionality as yesterday’s example implementing ActionListener. Again, we need to implement our server. The NewsServer class keeps hold of a few headlines whilst running a simple thread. Every now and again, the thread will pick-out a headline and send it to all observers.

public class NewsServer extends Observable implements Runnable {

 
private static Random rnd = new Random();

 
private static final String[] dummyHeadlines = { "War time bomber found on the Moon",
     
"There is no more news at the moment",
     
"Monster Raving Looney Party in landslide election win",
     
"Spam irradicated from the Internet", "Life found on Mars",
     
"Gramatical errors found in news headlines",
     
"Software project released on time and under budget." };

 
private boolean bRun;

 
public NewsServer() {
   
Thread t = new Thread(this);
    bRun =
true;
    t.start
();
 
}

 
public void stopServer() {
   
bRun = false;
 
}

 
/**
   * The run method for the thread. This chooses a headline and then sends it to each of the
   * observers. This is achieved by using a random number generator and, at the appropriate
   * juncture, updating the data value and notifying the observers of the change.
   */
 
public void run() {

   
while (bRun) {

     
if (isNewHeadline()) {
       
String headlineText = chooseHeadLine();
        notifyObservers
(headlineText);
     
}
     
sleep(1);
   
}
  }

 
private boolean isNewHeadline() {

   
// give ourselves a one in three
    // chance of distributing a story
   
return rnd.nextInt(3) == 1;
 
}

 
private String chooseHeadLine() {

   
// Next choose the headline id
   
int headlineId = rnd.nextInt(dummyHeadlines.length);
   
return dummyHeadlines[headlineId];
 
}

 
private void notifyObservers(String headlineText) {

   
// Update the data
   
setChanged(); // set changed flag
   
super.notifyObservers(headlineText); // do notification
 
}

 
private void sleep(int time) {

   
try {
     
SECONDS.sleep(time);
   
} catch (InterruptedException e) {
     
/* do nothing just continue */
   
}
  }
}

This code is complimented by the NewsClient, which implements the client code. In this case the implementation is simply overriding:

  public void update(Observable o, Object str)

The full client code is below:

public class NewsClient implements Observer {

 
/**
   * This is the implementation of the Observer interface - just one method. In this case all
   * we do is print the headlines to the screen. Note that the Observer class typically
   * implements some view on the data, whilst the Observable class implements the data and
   * its changes. Hence we may have multiple views on the data to one observable holding the
   * data.
   *
   *
@param o
   *            The observable object
   *
@param str
   *            The changed data - in this case cast to a string.
   */
 
public void update(Observable o, Object str) {
   
System.out.println("update: " + str.toString());
 
}

}

The NewsApp code holds the client and server together, demonstrating that your server code should be able to handle multiple observers.

public class NewApp {

 
/**
   * Note that the instances of observable/observer are created using either their base
   * classes or interfaces respectively. This means that we can combine the Observer pattern
   * with the factory pattern to hide the construction of the observers; the client code
   * doesn't really need to know which observers it's connected to.
   */
 
public static void main(String[] args) {

   
// This is the Observable object.
   
Observable n = new NewsServer();

   
// Create the observer
   
Observer o = new NewsClient();
    n.addObserver
(o);

   
// The observer should run until we kill the thread.
 
}
}

There has been some criticism of this implementation in that your observable class has to extend Observable. This, through Java's single inheritance, limits how you can use this implementation of the pattern as your observable class may have to extend another more relevant class within your business domain, though this may be remedied by separating your observable class from your domain class using aggregation; but more on that later... perhaps.

No comments: