Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Decorate your Java code

A look at the Decorator design pattern

  • Print
  • Feedback

Page 2 of 4

Alternatively, of course, you could write Example 1 like this:

LineNumberReader lrdr = new LineNumberReader(new FileReader(filename));


Both methods outlined above adhere to the same construction idiom: wrapping objects recursively -- a hallmark of the Decorator design pattern.

The code in Example 2 below shows how to instantiate and use a line number reader. The example reads lines of text from a file and prints each line with its line number:

Example 2. Using I/O decorators

try {
   LineNumberReader lrdr = new LineNumberReader(new FileReader(filename));
   for(String line; (line = lrdr.readLine()) != null;)rticle.txt {
      System.out.print(lrdr.getLineNumber() + ":\t" + line);
   }
}
catch(java.io.FileNotFoundException fnfx) {
   fnfx.printStackTrace();
}
catch(java.io.IOException iox) {
   iox.printStackTrace();
} 


Decorators represent a powerful alternative to inheritance. Whereas inheritance lets you add functionality to classes at compile time, decorators let you add functionality to objects at runtime.

Decorator statics and dynamics

Mechanical engineers study statics and dynamics. Statics analyze forces on objects that don't move very much -- bridges or buildings, for instance. Dynamics analyze forces on moving objects, typically in machines. The centrifugal forces on jet engine blades or clock pendulums are good examples.

Statics and dynamics have direct counterparts in the realm of software design patterns. Design pattern statics analyze class relationships specified at compile time, whereas design pattern dynamics analyze the runtime sequence of events in which objects participate. In this section, I show design pattern statics with UML class diagrams and design pattern dynamics with UML sequence diagrams.

Throughout the Java Design Pattern column, I will discuss the static and dynamic aspects of each design pattern we explore. Let's begin by examining the statics and dynamics of the pattern at hand -- Decorator.

Decorator statics

Decorators decorate an object by enhancing (or in some cases restricting) its functionality. Those objects are referred to as decorated. Figure 1 shows the static relationship between decorators and the decorated.

Figure 1. Decorator class diagram. Click on thumbnail to view full-size image.

Decorators extend the decorated class (or implement the decorated interface), which lets decorators masquerade as the objects they decorate. They also maintain a reference to a Decorated instance. That instance is the object that the decorator decorates. As an example of how the classes in the Decorator pattern relate, Figure 2 depicts the static relationships among four decorators from the java.io package:

  • BufferedReader
  • LineNumberReader
  • FilterReader
  • PushbackReader


Figure 2. I/O decorators

BufferedReader and FilterReader are decorators just like the one shown in Figure 1. Both classes extend the abstract Reader class, and both forward method calls to an enclosed Reader. Because they extend BufferedReader and FilterReader, respectively, LineNumberReader and PushbackReader are also decorators.

Decorator dynamics

At runtime, decorators forward method calls to the objects they decorate, as shown in Figure 3.

Figure 3. Decorator dynamics. Click on thumbnail to view full-size image.

Developers often refer to Decorators as wrappers because they wrap method calls to decorated objects. Figure 3 clearly depicts such wrapping. Figure 4 shows the dynamics of the code listed in Example 2.

Figure 4. I/O decorator dynamics

Now that we have a high-level understanding of Decorator's statics and dynamics, let's use an example to examine the implementation of the Decorator pattern.

Sort and filter decorators for Swing tables

The Decorator pattern excels at attaching functionality, such as sorting and filtering, to Swing tables. In fact, the Decorator pattern is flexible enough that we can attach functionality to any Swing table at runtime. Before we can discuss how decorators enhance Swing tables in detail, we must have a basic understanding of Swing tables; so, let's take a short detour.

Swing tables

Swing components are implemented with the Model-View-Controller (MVC) design pattern. Each component consists of a model that maintains data, views that display the data, and controllers that react to events. For example, Swing tables, instances of JTable, are commonly created with an explicit model. Example 3 lists an application that does just that. (Try to picture the table in Example 3 before viewing it in Figure 5.)

Example 3. Create a Swing table

import javax.swing.*;
import javax.swing.table.*;
public class Test extends JFrame {
   public static void main(String args[]) {
      Test frame = new Test();
      frame.setTitle("Tables and Models");
      frame.setBounds(300, 300, 450, 300);
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.show();
   }
   public Test() {
      TableModel model = new TestModel();   
      getContentPane().add(new JScrollPane(new JTable(model)));
   }
   private static class TestModel extends AbstractTableModel {
      final int rows = 100, cols = 10;
      public int    getRowCount()    { return rows; }
      public int    getColumnCount() { return cols; }
      public Object getValueAt(int row, int col) {
         return "(" + row + "," + col + ")";
      }
   }
}


The application creates a table with a model. That table is placed in a scrollpane and added to the frame's content pane. Figure 5 shows the application listed in Example 3.

Figure 5. A simple Swing table

The application shown above creates a table model with 100 rows and 10 columns. When asked for a cell value, the model creates a string that represents the cell's row and column. The application uses that model to construct a Swing table (an instance of JTable). Table models produce a table's data, in addition to metadata such as the number of rows and columns.

It's important to understand that Swing tables possess models that maintain a table's data. Table models, as evidenced in Example 3, do not have to actually store any data -- they must produce a value for a given row and column.

A sort decorator

With a basic understanding of the Decorator pattern and Swing tables, we can now implement a table sort decorator. First, we'll see an application that uses the decorator, followed by a discussion of the decorator's implementation.

  • Print
  • Feedback

Resources