October 21, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Working with Design Patterns: Observer

  • August 17, 2007
  • By Jeff Langr
  • Send Email »
  • More Articles »

In an earlier article on threading, I provided an implementation for a fax server (see Listing 1 for code implemented in the core classes). Within the server code, I directly embedded System.out.println statements. Although it's an acceptable technique for demonstration purposes, I rarely want to intermingle my server code and display output.

Listing 1: Fax server and client.

// Fax.java:
package waitperiod;

import java.util.concurrent.*;

public class FaxServer {
   private DelayQueue<FaxTransmission> queue =
      new DelayQueue<FaxTransmission>();
   private Dialer dialer;
   private Transmitter transmitter;

   public FaxServer(Dialer dialer, Transmitter transmitter) {
      this.dialer = dialer;
      this.transmitter = transmitter;
   }

   public void start() {
      new Thread(new Runnable() {
         public void run() {
            while (true) {
               try {
                  transmit(queue.take());
               }
               catch (InterruptedException e) {
               }
            }
         }
      }).start();
   }

   private void transmit(FaxTransmission transmission) {
      if (dialer.connect(transmission.getFax().to())) {
         System.out.printf("sending %s.", transmission);
         transmitter.send(transmission.getFax());
         System.out.println("completed");
      }
      else {
         System.out.printf("busy, queuing %s for resend%n",
                           transmission);
         transmission.setToResend();
         queue.add(transmission);
      }
   }

   public void send(Fax fax) {
      System.out.printf("queuing %s%n", fax);
      queue.add(new FaxTransmission(fax));
   }
}

// Client.java:
import java.util.*;
import util.*;

public class Client {
   public static void main(String[] args) {
      Fax fax1 = new Fax("1", "5555", "some message 1");
      Fax fax2 = new Fax("2", "6666", "some message 2");
      Fax fax3 = new Fax("3", "7777", "some message 3");
      Fax fax4 = new Fax("4", "8888", "some message 4");

      Dialer dialer = new Dialer() {
         private Random random = new Random();
         public boolean connect(String number) {
            return random.nextInt(5) == 0;
         }
      };

      Transmitter transmitter = new Transmitter()  {
         public void send(Fax fax) {
            for (int i = 0; i < 10; i++) {
               System.out.print(".");
               ThreadUtil.pause(1);
            }
         }
      };

      FaxServer server = new FaxServer(dialer, transmitter);
      server.start();
      server.send(fax1);
      ThreadUtil.pause(1);
      server.send(fax2);
      ThreadUtil.pause(1);
      server.send(fax3);
      ThreadUtil.pause(1);
      server.send(fax4);
   }
}

Separating user interface output from logic is almost always a good idea. It sets the stage for cheaper and safer maintenance in the future, when, for example, I want to change the fax server from a console application to something that interacts with a nice LCD output panel. It's also easier to write tests against single-purpose code. In contrast, having code that combines the two purposes of presentation and fax management is considerably more difficult to test.

Generally, I want the clients exercising my fax server to be the ones driving where the output should go and how it should be formatted. A simple solution would be to have the client object pass a reference to itself to the FaxServer. The FaxServer would call back to the client when anything of interest occurred.

But, such a simple solution is less than ideal. The client is already cognizant of the FaxServer, which is necessary. Now, the FaxServer would need to know the type of the client. Changes to the client could potentially ripple into the FaxServer. This sort of two-way dependency is, in most cases, a bad design choice.

The observer design pattern provides a simple solution: Instead of passing a client reference to the fax server, the client passes an abstraction of itself. It defines itself as an "observer" by implementing an interface (java.util.Observer), and then passes the interface reference to the fax server. The fax server knows only that it's interacting with an Observer instance, not a ConsoleClient or a FancyLcdPanelClient.

Use of observer is so common that Java provides a built-in implementation of the pattern. Java supplies the java.util.Observer interface, which defines the single method update (see Listing 2). It also supplies the java.util.Observable class. A class defines itself as the target of observation by extending Observable.





Page 1 of 3



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel