Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

Java Q&A


Apr99: Java Q&A

Steve is an independent contractor specializing in C++/Java/UNIX development. He also lectures in Object-Oriented Design and C++ programming at the Auckland Institute of Technology, New Zealand. Steve can be contacted at [email protected] John is Interactive Technology manager at Dave Clark Design Associates and principal lecturer at the Auckland Institute of Technology. He can be contacted at [email protected]


AJava applet may seem to be an independent program, but in one crucial aspect, it is not. If you change a static field of a class used within an applet, that change pervades all applets -- even if they are on different web pages. Although, as we showed in " Channels for Inter-Applet Communication" (DDJ, September 1998), this behavior opens up a channel for inter-applet communication, such effects can be as unwelcome as they are unexpected.

An example CAD system we wrote, called "WebCAD," which lets models be built and displayed in 3D, demonstrates these unwelcome effects. (The complete source code and related files for WebCAD are available electronically; see "Resource Center," page 5.) First, consider this system implemented as a stand-alone Java application. Figure 1 shows two instances of this application, in separate windows.

At its lowest level, WebCAD deals entirely in polygons (or planes). The model is displayed by drawing each plane in turn. Users can zoom in or out of the picture by changing the scale factor, set the display to either wireframe or solid-object mode, and spin the object around its x-and y-axes by dragging it with the mouse. All of these settings necessarily affect every plane in the model.

In WebCAD, we use static fields in the Plane class to express these display properties -- one for the scale factor, another for the display mode, and two together for the current viewing angle; see Listing One. This is the alternative to storing the display properties as nonstatic members in each Plane object, which would require iterating through all Plane objects and changing each individually -- a task that is both tedious and needlessly time consuming, since it would be meaningless for any two planes to be displayed with a different scale factor or viewing angle.

Used this way, static fields are fulfilling the role for which they were originally created in C++ (from which Java inherited them). According to Bjarne Stroustrup and Margaret Ellis in The Annotated C++ Reference Manual (Addison-Wesley, 1990), "A static member function or variable acts as a global for members of its class without affecting the rest of the program." They go on to say that since "a static data member [is] shared by all objects of a class in a program," their scope is limited to the executing program.

The consequence for WebCAD is that if we have two windows, each displaying separate instances of it (see Figure 1), we can change their display properties independently even while the planes in each separate model share the same values.

In contrast, if we make a direct translation of WebCAD to an applet and display separate instances of it on separate web pages, we cannot make independent changes to their display properties. The browser executes applets as separate threads in the same Java Virtual Machine, so all applets share the same values of the static fields of any common classes. The scope of the static fields is not limited to the applet object or its executing thread, but encompasses all objects of the applet's class, wherever they are instantiated by the browser.

Figure 2 shows two applets experiencing just this problem. The models should be in different scales and different display modes, but they both have identical appearances.

Reclaiming Static Semantics for Applets

What you need for applets, then, is some means of resolving the sibling rivalry that occurs when applets wish to have their own private copies of static fields. In this article, we present a workaround that lets the conventional semantics of static fields be reclaimed through the use of a specialized mechanism that mimics the behavior of static fields. More importantly, this workaround possesses the same meaning in both Java applications and applets.

This lets you develop classes that operate identically, whether instantiated within an application or an applet -- something not possible if the classes use static fields. First, we present our solution in code form, and then we abstract its essence in the form of a design pattern. We'll also provide a set of rules that may be mechanically applied to a class that uses static fields, which will convert it to a class that is portable between executing environments.

Mimicking Static Fields

Because static fields may not be used to store values particular to just one applet, it follows that these values need to be represented as instance variables in some object. The main Applet object is an excellent candidate for the custodian of these values, both because it sits at the root of the function call hierarchy and because there is only one per applet.

However, the corollary to this decision is that in some form these values have to appear in every stack frame from the topmost to the bottommost. We will discuss three ways to do this.

First, they may be passed as constructor arguments to each class (see Listing Two) in the composition hierarchy.

Eliminating Argument Clutter

The second solution seeks to avoid the passing of long lists of values to a particular class (such as Cube) with no other purpose than to allow that class to pass them on down. Instead, you can simply pass a reference to the applet itself; see Listing Three.

This approach of placing the values in the Applet object is adequate for many situations where objects depend upon global state values particular to just their applet. Consider, for example, an applet that supports an HTML parameter that allows sound effects to be turned on or off. The setting of this value needs to be available to a variety of methods in a number of different low-level classes. If the value is stored in the applet during its initialization, then all objects with access to the Applet object are able to query it.

We recommend that you routinely pass the main Applet object as a constructor argument to all helper classes. Then, when you discover you need to configure the applet's behavior at some low level based on a global value, you do not have the arduous task of retracing your steps back up the call hierarchy and inserting Applet references and constructor arguments into every intervening class.

Emphasizing Relationships Between Classes

The third approach is suitable for simple cases, particularly where global data (like sound-effects flags) are not clearly owned by any one class (other than the applet).

The disadvantage is that the Applet object, like the constructor argument list in our first solution, becomes cluttered with the shared values from all the other classes in the application that require applet-specific values to be stored. Also, there is no explicit relationship between the mysterious appearance of an instance variable in the Applet object and the class of the objects that depend on it.

WebCAD is a good example of this, as there is a clear family tie between the Plane objects and the global properties that affect them all. That structural relationship may be expressed by encapsulating the display properties in a separate class, an instance of which is placed in the main Applet object and passed (eventually) as a constructor argument to each Plane object; see Listing Four.

This relationship between a class (Foo) and the class that holds its static fields (FooProperties) mirrors the traditional relationship between a class and its static fields. According to Stroustrup and Ellis, "The association between the static members and their class is explicit and obvious, whereas the use of global variables and functions for similar purposes is neither."

Intermediate classes (such as Cube) still have the task of passing on the properties reference (though they have no personal interest in it), but at least the ultimate destination class is obvious and documented in the code; see Listing Five.

The class that makes use of the applet's properties object ends up holding a reference to it. The class (Plane, in this case; see Listing Six) refers to the display properties by prefixing them with the name of the properties object, but otherwise treats them just as if they were static fields of the same class -- which is effectively what they are, except they are not shared with other applets.

Independent Applets in Java

WebCAD is implemented as an applet that may be safely instantiated multiple times in the same browser window without any unpleasant inter-applet interference.

Figure 3 is a web page with two instances of the applet displayed. The instances have entirely different display properties: One has a wireframe appearance, the other is solid with hidden-line removal; one has been scaled down to 80 percent of its initial size; and the models are being viewed from different angles. This mechanism can be applied in any situation where a class wishes to have shared global values that are not affected by other applets, and may be generalized in the form of the Shared Property design pattern.

Intent. Allow the sharing of properties within a defined subset of the set of all objects instantiated from a class.

Motivation. It is primarily in the context of Java applets that the Shared Property pattern is most usefully implemented, since web browsers' implementations allow Java class variables to be shared across applet boundaries. The Shared Property pattern mimics the behavior of static fields in classes executed within independent Java applications.

Applicability. Use the Shared Property pattern when undesirable side effects are produced by allowing the complete set of instantiated class objects to share a property (such as when executing within a browser environment).

Structure. The Shared Property pattern is a composite of three classes: SharedProperties, PropertySharer, and PropertyOwner. Figure 4 shows the structure of the Shared Property pattern.

Participants. SharedProperties, (PlaneProperties), the attributes to be shared by a group of objects; PropertySharer (Plane), an object belonging to the group sharing the common attributes; PropertyOwner (WebCAD), the object responsible for the construction and storage of the common SharedProperties object.

Collaborations. Clients access shared properties via a reference to the unique SharedProperties object, instead of through a static class variable of the sharing class. Intermediate classes in the composition hierarchy are obliged to pass on that reference to the SharedProperties object until it reaches its ultimate destination in the PropertySharer class objects.

Consequences. The Shared Property pattern allows the conventional behavior of static class variables to be reclaimed in an environment where static variables do not have their traditional semantics.

Implementation. The steps to convert a class that uses static fields to one that implements the Shared Properties pattern are:

1. Extracting the shared properties. Extract any fields defined as static in the class to be converted (the PropertySharer) and place them in their own separate class (the SharedProperties class). The crucial step is to ensure that they are defined as regular members in the new class rather than as static fields.

2. Creating the shared properties object. Construct a single instance of the SharedProperties class as part of the applet class's construction and record it in a private instance variable. If the values for the initial state of this object are not yet known, simply create an object using default values. Being shared, changes made to this object are made immediately apparent to members of the PropertySharer class, so initializing the values in the SharedProperties object may be deferred until the point that the first PropertySharer object needs to use them.

3. Making the shared properties accessible. The SharedProperties object needs to be available wherever objects of the PropertySharer class are instantiated. The easiest way to achieve this is to pass a reference to the applet's SharedProperties object down the function call hierarchy through the constructors of any intermediate classes (or, alternatively, a reference to the Applet object, from which the SharedProperties object may be obtained).

4. Associating the objects with their shared properties. Simply provide an additional argument to each of the constructors for the PropertySharer class. A reference to the SharedProperties object should be stored in a private instance variable.

5. Implementing Accessors and Mutators. Any existing accessor (get methods) or mutator (set methods) functions in the PropertySharer class, along with any other references to the erstwhile static fields, are modified to reference the fields in the SharedProperty object by prefixing them with the name of the SharedProperty instance variable. The accessors and mutators remain in this class and are not relocated in the SharedProperties class, so that use of the design pattern is as transparent to users of the class as possible.

Conclusion

Classes that use static fields with their traditional semantics almost always give undesired behavior when instantiated in an applet environment. To produce portable code or to use static fields with their conventional meaning in applets, an alternative idiom is required.

We've presented two possible solutions. For simple cases -- and especially where a value is global to the applet and not particular to any one class -- the approach of storing the data directly in the main Applet object and passing the applet object down to the other helper classes is concise, effective, and extensible.

Where there is a deliberately exclusive relationship between a set of shared data and objects of a particular class, the Shared Property pattern is superior because, in addition to all the benefits of the first approach, it explicitly establishes a tightly coupled relationship between the two classes.

We recommend that you use either of the solutions we present even when producing classes targeted for stand-alone Java applications, so that if you wish to utilize those classes in an applet at a later time, you may reuse them freely without having to make any modifications.

The working Java applets from this article may be viewed at http://effectivejava .com/steve/iapps/ and at http://www.dcda .co.nz/jmc/iapps/.

DDJ

Listing One

class Plane {
    static int displayMode;
    static double scaleFactor;
    static double rotation, elevation;
}

Back to Article

Listing Two

class WebCAD extends Applet {
    WebCAD() {
        cubes[0] = new Cube(displayMode, scaleFactor, rotation,
            elevation, /*other args*/);
        ...
    }
}
class Cube {
    Cube(int displayMode, double scaleFactor, double rotation,
            double elevation, /*other args*/) {
        topFace = new Plane(displayMode, scaleFactor, rotation,
            elevation, /*other args*/);
        ...
    }
}

Back to Article

Listing Three

class WebCAD extends Applet {
    WebCAD() {
        cubes[0] = new Cube(this, /*other args*/);
        ...
    }
}
class Cube {
    Cube(Applet applet, /*other args*/) {
        topFace = new Plane(applet, /*other args*/);
        ...
    }
}
class Plane {
    Plane(Applet applet, /*other args*/) {
        this.applet = (WebCAD) applet;
        ...
    }
    void draw(Graphics g) {
        switch (applet.displayMode) {
            case DM_WIRE_FRAME:
            case DM_SOLID_OBJECT:
        }
    }
    private WebCAD applet;
}

Back to Article

Listing Four

class PlaneProperties {
    int displayMode;
    double scaleFactor;
    double rotation;
    double elevation;
    PlaneProperties(int displayMode, double scaleFactor,
            double rotation, double elevation) {
        this.displayMode = displayMode;
        this.scaleFactor = scaleFactor;
        this.rotation = rotation;
        this.elevation = elevation;
    }
}
class WebCAD extends Applet {
    WebCAD() {
        planeProperties = new PlaneProperties(Plane.DM_WIRE_FRAME,
            1.0, INITIAL_ROTATION, INITIAL_ELEVATION);
        cubes[0] = new Cube(planeProperties, /*other args*/);
    }
    private PlaneProperties planeProperties;
}

Back to Article

Listing Five

class Cube {
    Cube(PlaneProperties planeProperties, /*other args*/) {
        topFace = new Plane(planeProperties, /*other args*/);
        ...
    }
}

Back to Article

Listing Six

class Plane {
    Plane(PlaneProperties planeProperties, /*other args*/) {
        this.planeProperties = planeProperties;
        ...
    }
    void draw(Graphics g) {
        switch (planeProperties.displayMode) {
            case DM_WIRE_FRAME:
            case DM_SOLID_OBJECT:
        }
    }
    private PlaneProperties planeProperties;
}


















Back to Article


Copyright © 1999, Dr. Dobb's Journal

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.