next up previous
Next: 4. Conclusions Up: Common Programming Idioms and Previous: 2.3.3.3 Section summary.

   
3. Patterns in Java APIs

We attempt to understand the structure and the content of Java APIs from a design patterns perspective. That is, we cite examples of application of patterns to the design of APIs, and motivate inclusion of certain classes as tools for implementing specific design patterns [17]. This further elucidates the application of patterns for code reuse. Especially important is the role of interfaces and abstract classes in structuring various designs. For instance, implementing the interface java.util.Iterator, which declares methods hasNext(), next(), and remove(), enables a client to iterate over elements of different collections. The following sampling of examples is far from complete and includes some given in [11].

package java.io:

The abstract class java.io.FilterInputStream and its concrete subclasses given below provide a classic example of support for the Decorator pattern, which enables addition of responsibilities to an instance transparently.

class java.io.FilterInputStream 
                  class java.io.BufferedInputStream 
                  class java.util.zip.CheckedInputStream 
                  class java.io.DataInputStream 
                  class java.security.DigestInputStream 
                  class java.util.zip.InflaterInputStream 
                              class java.util.zip.GZIPInputStream 
                              class java.util.zip.ZipInputStream 
                  class java.io.LineNumberInputStream 
                  class java.io.PushbackInputStream

For example, in order to read data from a compressed file ``foo.zip'', one can read data from the stream defined below:

  DataInputStream input = new DataInputStream ( 
                            new ZipInputStream ( 
                                new FileInputStream ("foo.zip") ) );

Class java.io.FilterInputStream is also an java.io.InputStream and contains a protected field in of type InputStream to which all the method calls are delegated. The call to read()-method is propagated through a chain of filters (instances of concrete subclasses of FilterInputStream), and each one contributes its mite. In other words, the Decorator pattern adds new behavior without changing the interface. (Similarly, for output using the class hierarchy rooted at class java.io.FilterOutputStream.)

The Strategy pattern has been used in class java.util.zip.CheckedInputStream and class java.util.zip.CheckedOutputStream, to enable computation of checksums on byte streams in one of two ways. In particular, the constructors contain a parameter of type Checksum that can be passed an instance of class Adler32 or class CRC32, which encapsulate algorithms to determine the checksum. The classes Adler32 and CRC32 implement interface Checksum.

The abstract class InputStream and its concrete subclasses provide an example of a creational pattern (such as Abstract factory, Factory method etc), which enables unification of input from a byte-array, a file, a network connection, a persistent storage, a pipe, a string, etc.

class java.io.InputStream 
              class java.io.ByteArrayInputStream 
              class java.io.FileInputStream 
              class java.io.FilterInputStream 
              class java.io.ObjectInputStream
              class java.io.PipedInputStream 
              class java.io.SequenceInputStream 
              class java.io.StringBufferInputStream

The class java.io.SequenceInputStream also illustrates the Composite pattern because it can be used to create an input stream by concatenating multiple input streams [11].

A java.io.InputStreamReader translates a byte stream into a character stream, and a java.io.OutputStreamWriter translates a character stream into a byte stream. These classes exemplify the Adapter pattern. In particular, they change input/output stream interfaces to the required reader/writer interfaces [11].

Class StreamTokenizer is an example of a Prototype pattern that can be customized with respect to the end of line characters, comment delimiters, white space charaters, quote characters, etc.

packages java.awt and java.applet:
The application of Composite pattern in Abstract windowing toolkit is discussed in Section [*].

The Abstract Factory pattern enables isolating a set of classes completely from their concrete implementations. This pattern has been applied in porting Java 1.1 AWT onto different platforms (such as Windows, UNIX, Apple Macintosh). The users program using the APIs in java.awt, while the class java.awt.Toolkit joins the platform-independent classes in java.awt with their platform specific counterparts in java.awt.peer. Other examples of the Abstract Factory pattern are the interfaces class FontMetrics, class AppletContext, etc that provide a mechanism for creating instances of related objects without specifying a concrete implementation.

The Bridge pattern decouples abstraction from the implementation so that both can vary independently. Java 2 Swing consists of a special cross-platform look and feel (called Java Look and Feel) that always presents a set of components with the same appearance and behavior no matter what OS it is running on, and it also provides a single API capable of supporting multiple look-and-feel (such as Windows, Motif) on any platform (such as Windows, Mac), to aid developers and end-users. The general mechanism is called Pluggable Look and Feel Architecture where both presentation and behavior are separate and replaceable ("pluggable"). This approach enables one to specify any of the several look and feel designs for an application-or even create a new look and feel.

The Java 1.0 event model and the Java 1.1 low-level event handling uses Chain of responsibility pattern to process events. An event generated by a sender is passed through a chain of components (defined by the containment hierarchy) till it is caught and handled. This implicit propagation of events improves the structure of event-handling code.

In Java 1.1 delegation-based event model, an event object is passed from a source to one or more listeners by invoking an event-specific method (with the event object as an argument) on a listener. A listener implements an appropriate interface and registers itself with the source. Java uses simple and consistent naming convention for related events, interfaces, and methods. For instance, a listener for an event of class EVevent implements interface EVListener. The source of the event contains methods such as addEVListener, removeEVListener etc. Some listener interfaces require several event handlers, while typically only a few of the handlers will contain non-trivial body. So, Java provides adapter classes EVAdapter that implement the corresponding interface EVListener with all handlers containing empty bodies. These adapter classes are meant to be extended to provide non-trivial event handlers, possibly using inner class construct. This is a simple example of the application of Adapter pattern. See classes ComponentAdapter, ContainerAdapter, FocusAdapter, KeyAdapter, MouseAdapter, MouseMotionAdapter, WindowAdapter in package java.awt.event for details.

As discussed above, the Java 1.1 event hierarchy and event processing model facilitates multiple receiver subscription and subsequent sender notification to all of them, in response to a change in the sender, and consequent updates to the receivers. This is reminiscent of the Observer pattern.

The AWT components and the events are object-oriented and their use in an application exemplifies the Command pattern [12]. The Command pattern encourages encapsulation of a request as an object to control their selection and sequencing, and for logging, undoing, and similar manipulations. Of particular importance is its use in the context of ``undo-redo'' facility in text editors as implemented in package javax.swing.undo of Java 2, or as developed in Chapter 21 of [27].

The LayoutManagers, that specify the details of how components are laid out in a frame or a panel, exemplify the Strategy pattern [11]. The interface java.awt.LayoutManager is implemented by classes such as java.awt.BorderLayout, java.awt.FlowLayout, java.awt.GridLayout, java.awt.GridBagLayout, etc that encapsulate the placement algorithms.

The Applet class is an example of a framework that defines and encapsulates the interaction between a Java-enabled browser and a Java applet. It can be customized for different tasks by redefining Applet methods such as init(), start(), paint(), stop(), destroy(), etc.

packages java.beans and java.lang.reflect:

A bean is a class that follows certain naming conventions. Adhereing to these conventions enhances readability and reusability of classes, and reduces the need for adapters. The meta-programming facilities in java.lang.reflect permit run-time inspection, construction, and execution of programs. (See also class java.beans.Introspector.) This enables automatic manipulation of classes using visual development tools. The customization of a bean object using a visual tool embodies the Prototype pattern.

Smalltalk's MVC (model-view-controller) architecture consists of the application objects (the model), the screen presentation (the view), and the controller that takes user inputs on the view and translates them into changes in the model. The Observer pattern solves the problem of updating several listener objects in response to a change in the source object's state. The utility classes java.beans.PropertyChangeSupport and java.beans.VetoableChangeSupport facilitate management of communication between the various objects in response to events. In particular, it keeps track of listener objects that want to be informed when a change occurs in the source object and notifies them.

The Mediator pattern promotes loose coupling among objects, by avoiding explicit connections between the participants. This pattern can be used to introduce event queueing and event filtering between the source and the eventual listeners to enhance flexibility. The classes in java.beans and java.lang.reflect, in conjunction with Java 1.1 event model, support the mediator pattern [15].

packages java.net and java.rmi:
The java.net package supports traditional client-server paradigm and network programming by implementing TCP/IP and UDP protocols. The networking facilities are carefully packaged behind suitably designed classes that illustrate the facade pattern.

The java.rmi package simplifies distributed computing through remote method invocation. A client (running in a JVM) can invoke a method on a remote server object (running in a separate JVM, possibly on a different machine altogether) as if it were a local object. To implement this, Java employs the Proxy pattern. The client holds a reference to a local object that acts as a proxy for the remote object. This proxy implements the (remote) interfaces of the remote object, forwards all calls to that server object, and decodes its responses. In fact, the Proxy pattern is central to many distributed systems including the JINI system [35].

packages java.lang:
The intern() method in class String returns a canonical representation for the string object. In particular, if two string objects have the same state (sequence of characters), then their interned versions will be the same object. This is an application of the Flyweight pattern to improve efficiency. (A flyweight is a shared object that can be used in multiple contexts simultaneously.)

Another example of the application of Flyweight pattern is the sharing of information about a class among all its instances using an object of type Class [11].

The Singleton pattern ensures that exactly one instance of a class is created, and provides a global point of access. Class Runtime exemplifies this situation.

The procedural style and the object-oriented style provide two different schemes for organizing heterogeneous data that share common-behavior. The addition of a new operation (resp. representation) is incremental in procedural (resp. object-oriented) style, but is not incremental in object-oriented (resp. procedural) style. The Visitor pattern permits clustering together of code for an operation for all the variants of a type, thereby enabling more graceful accommodation of a new operation [16].


next up previous
Next: 4. Conclusions Up: Common Programming Idioms and Previous: 2.3.3.3 Section summary.
T. K. Prasad