|
|
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
Not long ago my clutch gave out, so I had my Jeep towed to a local dealership. I didn't know anybody at the dealership, and none of them knew me, so I gave them my telephone number so they could notify me with an estimate. That arrangement worked so well we did the same thing when the work was finished. Because this all turned out perfectly for me, I suspect the service department at the dealership employs the same pattern with most of its customers.
This publish-subscribe pattern, where an observer registers with a subject and subsequently receives notifications, is quite common, both in everyday life and in the virtual world of software development. In fact, the Observer pattern, as it is known, is one of the linchpins of object-oriented software development because it lets dissimilar objects communicate. That ability lets you plug objects into a framework at runtime, which allows for highly flexible, extensible, and reusable software.
Note: You can download this article's source code from Resources.
In Design Patterns, the authors describe the Observer pattern like this:
Define a one to many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
The Observer pattern has one subject and potentially many observers. Observers register with the subject, which notifies the observers when events occur. The prototypical Observer example is a graphical user interface (GUI) that simultaneously displays two views of a single model; the views register with the model, and when the model changes, it notifies the views, which update accordingly. Let's see how it works.
The application shown in Figure 1 contains one model and two views. The model's value, which represents image magnification, is manipulated by moving the slider knob. The views, known as components in Swing, are a label that shows the model's value and a scroll pane that scales an image in accordance with the model's value.
Figure 1. Observers in action. Click on thumbnail to view full-size image.
The model in the application is an instance of DefaultBoundedRangeModel(), which tracks a bounded integer value—in this case from 0 to 100—with these methods:
int getMaximum()int getMinimum()int getValue()boolean getValueIsAdjusting()int getExtent()void setMaximum(int)void setMinimum(int)void setValue(int)void setValueIsAdjusting(boolean)void setExtent(int)void setRangeProperties(int value, int extent, int min, int max, boolean adjusting)void addChangeListener(ChangeListener)void removeChangeListener(ChangeListener)As the last two methods listed above indicate, instances of DefaultBoundedRangeModel() support change listeners. Example 1 shows how the application takes advantage of that feature:
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class Test extends JFrame {
private DefaultBoundedRangeModel model =
new DefaultBoundedRangeModel(100,0,0,100);
private JSlider slider = new JSlider(model);
private JLabel readOut = new JLabel("100%");
private ImageIcon image = new ImageIcon("shortcake.jpg");
private ImageView imageView = new ImageView(image, model);
public Test() {
super("The Observer Design Pattern");
Container contentPane = getContentPane();
JPanel panel = new JPanel();
panel.add(new JLabel("Set Image Size:"));
panel.add(slider);
panel.add(readOut);
contentPane.add(panel, BorderLayout.NORTH);
contentPane.add(imageView, BorderLayout.CENTER);
model.addChangeListener(new ReadOutSynchronizer());
}
public static void main(String args[]) {
Test test = new Test();
test.setBounds(100,100,400,350);
test.show();
}
class ReadOutSynchronizer implements ChangeListener {
public void stateChanged(ChangeEvent e) {
String s = Integer.toString(model.getValue());
readOut.setText(s + "%");
readOut.revalidate();
}
}
}
class ImageView extends JScrollPane {
private JPanel panel = new JPanel();
private Dimension originalSize = new Dimension();
private Image originalImage;
private ImageIcon icon;
public ImageView(ImageIcon icon, BoundedRangeModel model) {
panel.setLayout(new BorderLayout());
panel.add(new JLabel(icon));
this.icon = icon;
this.originalImage = icon.getImage();
setViewportView(panel);
model.addChangeListener(new ModelListener());
originalSize.width = icon.getIconWidth();
originalSize.height = icon.getIconHeight();
}
class ModelListener implements ChangeListener {
public void stateChanged(ChangeEvent e) {
BoundedRangeModel model =
(BoundedRangeModel)e.getSource();
if(model.getValueIsAdjusting()) {
int min = model.getMinimum(),
max = model.getMaximum(),
span = max - min,
value = model.getValue();
double multiplier = (double)value / (double)span;
multiplier = multiplier == 0.0 ?
0.01 : multiplier;
Image scaled = originalImage.getScaledInstance(
(int)(originalSize.width * multiplier),
(int)(originalSize.height * multiplier),
Image.SCALE_FAST);
icon.setImage(scaled);
panel.revalidate();
panel.repaint();
}
}
}
}
stateChanged() to determine the model's new value.Swing is a heavy user of the Observer pattern—it implements more than 50 event listeners for implementing application-specific
behavior, from reacting to a pressed button to vetoing a window close event for an internal frame. But Swing is not the only
framework that puts the Observer pattern to good use—it's widely used in the Java 2 SDK; for example: the Abstract Window
Toolkit, the JavaBeans framework, the javax.naming package, and input/output handlers.