Using the Decorator Pattern
Pages: 1, 2
The constructor assigns the decorated component to child and
adds it as a child component of the decorator. Note that here we use a
BorderLayout as the layout for the decorator. This means the added
JComponent will occupy the whole area of the decorator.
Now, take a look at the paint method. It first calls the
parent class' paint method. This will paint the decorator as well as
all of its children. We then draw a rectangle around the decorator area, after
first getting the height and the width of the decorator.
Figure 1 shows a JFrame with three components:
- An instance of
JBorderLabel. - A decorated
JLabel. - A decorated
JCheckBox.

Figure 1 -- comparing subclassing and the Decorator pattern
As Figure 1 shows, there is no difference in appearance between an instance
of JBorderLabel and a decorated JLabel. This
demonstrates that the Decorator pattern can be used as an alternative to
subclassing. The third component proves that you can use the same Decorator to
extend the behavior of an instance of a different class. In this sense, the
decorator is superior, because you only need to create one class
(BorderDecorator) to extend the functionality of objects of many
different types.
Listing 3 gives the the code of the JFrame in Figure 1.
Listing 3 -- using the BorderDecorator class
package decorator;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Frame1 extends JFrame {
JBorderLabel label1 =
new JBorderLabel("JLabel Subclass");
BorderDecorator label2 =
new BorderDecorator(new JLabel("Decorated JLabel"));
BorderDecorator checkBox1 =
new BorderDecorator(new JCheckBox("Decorated JCheckBox"));
public Frame1() {
try {
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
getContentPane().setLayout(null);
label1.setBounds(new Rectangle(10, 10, 120, 25));
this.getContentPane().add(label1, null);
label2.setBounds(new Rectangle(10, 60, 120, 25));
this.getContentPane().add(label2, null);
checkBox1.setBounds(new Rectangle(10, 110, 160, 25));
this.getContentPane().add(checkBox1, null);
}
catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Frame1 frame1 = new Frame1();
frame1.setBounds(0, 0, 200, 200);
frame1.setVisible(true);
}
}
The ResizableDecorator Class
The ResizableDecorator is a different type of decorator. It
does not override the paint method of its parent class; instead,
it adds a small button that, when clicked, resizes and restores itself. Listing
4 presents the code of the ResizableDecorator class.
Listing 4 -- the ResizableDecorator class
package decorator;
import javax.swing.*;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.Rectangle;
public class ResizableDecorator extends JComponent {
// decorated component
protected JComponent child;
private JButton button = new JButton();
boolean minimum = false;
private Rectangle r;
public ResizableDecorator(JComponent component) {
child = component;
this.setLayout(new BorderLayout());
this.add(child);
child.setLayout(null);
button.setBounds(0, 0, 8, 8);
child.add(button);
button.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
button_actionPerformed(e);
}
});
}
void button_actionPerformed(ActionEvent e) {
if (minimum) {
this.setBounds(r);
}
else {
r = this.getBounds();
this.setBounds(r.x, r.y, 8, 8);
}
minimum = !minimum;
}
}
First note that this decorator also extends JComponent, and its
constructor accepts a JComponent. In addition to a variable called
child to reference the decorated component, it also declares a
JButton.
The first three lines of its constructor add the decorated component as the child component of the decorator.
child = component;
this.setLayout(new BorderLayout());
this.add(child);
It then adds the JButton as a child component of the decorated
component. The child's layout is null. The JButton will have a
dimension of 8 x 8 pixels and is added to the left-hand corner of the decorated
component.
child.setLayout(null);
button.setBounds(0, 0, 8, 8);
child.add(button);
Finally, it adds a listener to the JButton.
button.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
button_actionPerformed(e);
}
});
When added to a container, the component decorated using
ResizableDecorator will have a small button on its left-hand
corner. When clicked, this button will execute the code inside of the
button_actionPerfomed method. First, the method checks the value of
minimum. This boolean indicates whether or not the decorator size
is at its minimum. Initially, the value is false, so the else block is
executed.
else {
r = this.getBounds();
this.setBounds(r.x, r.y, 8, 8);
}
It assigns the current dimension to the Rectangle r and sets the bounds to 8 x 8, with the left-hand corner remaining at the current position. Then, it toggles the value of
minimum.
minimum = !minimum;
When the JButton is clicked for the second time, the value of
minimum is true. This time, the if block is executed:
if (minimum) {
this.setBounds(r);
}
The code will restore the decorator to its previous size.
Figure 2 shows a JFrame with three decorated components, and
its code is given in Listing 5.

Figure 2 -- three components decorated using ResizableDecorator
Listing 5 -- using the ResizableDecorator class
package decorator;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Frame2 extends JFrame {
ResizableDecorator label1 =
new ResizableDecorator(new JLabel(" Label1"));
ResizableDecorator button1 =
new ResizableDecorator(new JButton("Button"));
BorderDecorator label2 =
new BorderDecorator(new ResizableDecorator(
new JLabel(" doubly decorated")
));
public Frame2() {
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Frame2 frame = new Frame2();
frame.setBounds(0, 0, 200, 200);
frame.setVisible(true);
}
private void jbInit() throws Exception {
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.getContentPane().setLayout(null);
label1.setBounds(new Rectangle(10, 10, 120, 25));
label2.setBounds(new Rectangle(10, 60, 120, 25));
button1.setBounds(new Rectangle(10, 110, 120, 25));
this.getContentPane().add(label1, null);
this.getContentPane().add(label2, null);
this.getContentPane().add(button1, null);
}
}
Note that you can decorate a JComponent using one or more
decorators, just like the JLabel label2 in Listing 5.
Also, if you need to work with many decorators, you may want to consider having
an abstract Decorator class from which all decorator classes
derive. This way, you can put the common methods in the abstract class.
Summary
This article has compared subclassing with the Decorator pattern, giving two examples of decorators in Swing. While the use of the Decorator pattern is very common to change the visual look of a Swing component, its use is not limited to changing the user interface.
Budi Kurniawan is a senior J2EE architect and author.
Return to ONJava.com.
-
Best way of extending
2005-03-15 07:02:18 Puneeth [View]
-
Great article, what about listeners
2004-12-13 08:35:02 asra7 [View]
-
Decorator pattern, SWING or ....
2004-10-20 09:02:00 Nedda [View]
-
the order of ResizableDecorator and BorderDecorator
2004-08-24 05:35:10 QW [View]
-
Decorator should be investigated more
2003-03-10 03:32:34 mcostabile [View]
-
Decorator should be investigated more
2003-11-11 02:59:00 anonymous2 [View]
-
Perfect timing
2003-03-01 03:11:54 anonymous2 [View]
-
good article
2003-02-14 02:03:31 mkj6 [View]
-
Rehashes old ideas
2003-02-07 08:20:06 anonymous2 [View]
-
Limited application re GUI widgets
2003-02-07 02:24:04 davidccrane [View]