ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Data Models for Desktop Apps Data Models for Desktop Apps

by Andrei Cioroianu
06/02/2004

This is the third article in a series that presents the prototype of a Java desktop application called JImaging. The first article described the three major Java GUI toolkits: AWT, Swing, and SWT. In the second article, I introduced the prototype, with its classes and packages; I presented the code of the Main class and I explained how I made several important technical decisions. In this article, I'll describe the data model of the prototype and I'll show how to use it.

What are Data Models and Why Use Them?

The term "model" means various things in different contexts. Java developers use it frequently when they discuss the Model-View-Controller (MVC) pattern, but you may also hear this term when somebody talks about JavaBeans, whose specification defines a "component model." In this article, a data model means an object structure keeping the data of the entire application or just the data visualized by a single GUI component.

JavaBeans are serializable components that expose a set of properties accessible with get and set methods. They may also have public methods, but instead of calling each other's methods, JavaBeans communicate through events. This requires more coding since it involves the so-called event listener interfaces and event classes, but these features make the components independent and reusable. All GUI components of AWT and Swing are JavaBeans. The java.awt.event and javax.swing.event packages define events and listeners such as ActionEvent and ActionListener.

JavaBeans are also used on the server side in JavaServer Pages (JSP). This is possible because JavaBeans don't have to use any specific GUI API. Non-GUI JavaBeans usually act as data models, and their public methods may be used to implement the application's logic, processing the data. There is nothing stopping us from using non-GUI JavaBeans on the client side, in desktop applications.

Related Reading

Java Cookbook
By Ian F. Darwin

Separating the data model from the GUI that visualizes the data has many benefits:

  • You may change the model's internals without affecting the GUI.
  • Having all of the data in one place makes its processing easier than in the case where separate GUI components keep related pieces of information.
  • You may enhance the GUI without breaking the model's mechanisms, such as data persistence (loading and saving), data communication protocols, data encryption, etc.
  • Independent GUI components can be used to view the same model or just some of its parts.

The model-view separation seems to increase the application's complexity because you have to implement the event-based communication. This requires a bit more coding and maybe a few more classes and interfaces, but the complexity is actually reduced because there are fewer dependencies between the components of the application. If implemented correctly, the data model doesn't normally have to know anything about the GUI that uses it. This makes the code more maintainable and reusable.

In addition, the application logic code, which processes the model's data, doesn't have to notify the GUI when the data is updated. Instead, the interested GUI components register themselves as event listeners to the data model, which notifies them through a common listener interface. When new components enhance the user interface, the model's code doesn't have to be changed. If the data model is enriched, existing components can be easily modified to take advantage of the new features.

Developing Data Models

The JImaging prototype has a custom component called PaintView, which allows users to draw annotations on an image, such as rectangles, ellipses, and lines. The prototype also can be used to add text notes, but these are handled by the MainPanel class, which extends JDesktopPane. The information about all annotations, including the text notes, are managed by an instance of the PaintModel class.

Extending the PropertyChangeSupport Class

The java.beans package contains a few classes that implement a generic mechanism that allows a JavaBean to notify other components when the values of its properties are changed. The components interested in getting notifications must implement the PropertyChangeListener interface, which has one method, named propertyChange(). This method takes a PropertyChangeEvent parameter whose methods return the property's name, the old value, and the new value.

The PropertyChangeSupport class provides the addPropertyChangeListener() and removePropertyChangeListener() methods for registering and unregistering event listeners. The support class also has several firePropertyChange() methods that call the propertyChange() method of the registered listeners. Therefore, you can notify the event listeners about a property change with one of the firePropertyChange() methods.

The PaintModel class extends PropertyChangeSupport because it needs its listener registration and event-firing mechanisms. If a JavaBean must extend another class, you can still use PropertyChangeSupport. In this case, your JavaBean would use a PropertyChangeSupport instance and would need to define its own addPropertyChangeListener() and removePropertyChangeListener() methods that would delegate those calls to the PropertyChangeSupport instance.

The PaintModel class defines a String constant for each property because the methods of PropertyChangeSupport work with property names:

package com.devsphere.articles.desktop.paint;
...
import java.beans.PropertyChangeSupport;
...
public class PaintModel extends PropertyChangeSupport {
    public static final String BACK_COLOR_PROPERTY
        = "BACK_COLOR_PROPERTY";
    public static final String BACK_IMAGE_PROPERTY
        = "BACK_IMAGE_PROPERTY";
    public static final String ZOOM_FACTOR_PROPERTY
        = "ZOOM_FACTOR_PROPERTY";
    public static final String TOOL_CLASS_PROPERTY
        = "TOOL_CLASS_PROPERTY";
    public static final String TOOL_COLOR_PROPERTY
        = "TOOL_COLOR_PROPERTY";
    public static final String TOOL_STROKE_PROPERTY
        = "TOOL_STROKE_PROPERTY";
    public static final String LAST_TOOL_PROPERTY
        = "LAST_TOOL_PROPERTY";
    ...
} 

Using String constants is not very elegant, but it's the price we have to pay for using a generic class such as PropertyChangeSupport. Most Swing data models define their own event objects and listener interfaces in the javax.swing.event package, such as TableModelListener and TreeModelListener. It doesn't make sense to design custom event listeners for a simple prototype, but this is necessary when developing reusable GUI components.

The Properties of the PaintModel Class

The PaintModel class has several properties, as described in the following table:

Property Name Property Type Description
backColor Color Background color
backImage Image Image being annotated
zoomFactor float Zoom factor
toolClass Class Current tool used for painting
toolColor Color Current color used for painting
toolStroke Stroke Current stroke used for painting
lastTool AbstractTool The last added painting tool
toolIterator Iterator Iterator for all painting tool objects
(see below for more details)

The values of these properties, except for those of lastTool and toolIterator, are kept in private fields, and are initialized in the PaintModel() constructor:

public static final Color BACK_COLOR_INIT_VALUE = Color.white;
public static final Image BACK_IMAGE_INIT_VALUE = null;
public static final float ZOOM_FACTOR_INIT_VALUE = 1.0f;
public static final Class TOOL_CLASS_INIT_VALUE = NoteTool.class;
public static final Color TOOL_COLOR_INIT_VALUE = Color.black;
public static final BasicStroke TOOL_STROKE_INIT_VALUE = 
       new BasicStroke(5, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
 
private Color backColor;
private Image backImage;
private float zoomFactor;
private Class toolClass;
private Color toolColor;
private Stroke toolStroke;
private LinkedList toolList;
 
public PaintModel(Object source) {
   super(source);
   backColor = BACK_COLOR_INIT_VALUE;
   backImage = BACK_IMAGE_INIT_VALUE;
   zoomFactor = ZOOM_FACTOR_INIT_VALUE;
   toolClass = TOOL_CLASS_INIT_VALUE;
   toolColor = TOOL_COLOR_INIT_VALUE;
   toolStroke = TOOL_STROKE_INIT_VALUE;
   toolList = new LinkedList();
}

Each property has a get method that returns its value and a set method that changes the property's value and notifies the registered listeners. For example, the getZoomFactor() and setZoomFactor() methods are implemented with the following code:

public float getZoomFactor() {
   return zoomFactor;
}
 
public void setZoomFactor(float newZoomFactor) {
   float oldZoomFactor = zoomFactor;
   zoomFactor = newZoomFactor;
   firePropertyChange(ZOOM_FACTOR_PROPERTY, new Float(oldZoomFactor),
                      new Float(newZoomFactor));
}

The firePropertyChange() method calls the propertyChange() method of the listeners registered with addPropertyChangeListener().

Pages: 1, 2

Next Pagearrow