Treat primitive and composite objects the same way

JavaWorld|Sep 13, 2002 1:00 AM
PT

The other day I was listening to National Public Radio's Car Talk, a popular weekly broadcast during which callers ask questions about their vehicles. Before every program break, the show's hosts ask callers to dial 1-800-CAR-TALK, which corresponds to 1-800-227-8255. Of course, the former proves much easier to remember than the latter, in part because the words "CAR TALK" are a composite: two words that represent seven digits. Humans generally find it easier to deal with composites, rather than their individual components. Likewise, when you develop object-oriented software, it's often convenient to manipulate composites just like you manipulate individual components. That premise represents the fundamental principle of the Composite design pattern, the topic of this Java Design Patterns installment.

The Composite pattern

Before we dive into the Composite pattern, I must first define composite objects: objects that contain other objects; for example, a drawing may be composed of graphic primitives, such as lines, circles, rectangles, text, and so on.

Java developers need the Composite pattern because we often must manipulate composites exactly the same way we manipulate primitive objects. For example, graphic primitives such as lines or text must be drawn, moved, and resized. But we also want to perform the same operation on composites, such as drawings, that are composed of those primitives. Ideally, we'd like to perform operations on both primitive objects and composites in exactly the same manner, without distinguishing between the two. If we must distinguish between primitive objects and composites to perform the same operations on those two types of objects, our code would become more complex and more difficult to implement, maintain, and extend.

In Figure 1's class diagram, I used class names from Design Pattern's Composite pattern discussion: Component represents a base class (or possibly an interface) for primitive objects, and Composite represents a composite class. For example, the Component class might represent a base class for graphic primitives, whereas the Composite class might represent a Drawing class. Figure 1's Leaf class represents a concrete primitive object; for example, a Line class or a Text class. The Operation1() and Operation2() methods represent domain-specific methods implemented by both the Component and Composite classes.

The Composite class maintains a collection of components. Typically, Composite methods are implemented by iterating over that collection and invoking the appropriate method for each Component in the collection. For example, a Drawing class might implement its draw() method like this:

For every method implemented in the Component class, the Composite class implements a method with the same signature that iterates over the composite's components, as illustrated by the draw() method listed above.

The Composite class extends the Component class, so you can pass a composite to a method that expects a component; for example, consider the following method:

// This method is implemented in a class that's unrelated to the
// Component and Composite classes
public void repaint(Component component) {
// The component can be a composite, but since it extends
// the Component class, this method need not
// distinguish between components and composites
component.draw();
}

The preceding method is passed a component—either a simple component or a composit—then it invokes that component's draw() method. Because the Composite class extends Component, the repaint() method need not distinguish between components and composites—it simply invokes the draw() method for the component (or composite).

Figure 1's Composite pattern class diagram does illustrate one problem with the pattern: you must distinguish between components and composites when you reference a Component, and you must invoke a composite-specific method, such as addComponent(). You typically fulfill that requirement by adding a method, such as isComposite(), to the Component class. That method returns false for components and is overridden in the Composite class to return true. Additionally, you must also cast the Component reference to a Composite instance, like this:

Notice that the addComponent() method is passed a Component reference, which can be either a primitive component or a composite. Because that component can be a composite, you can compose components into a tree structure, as indicated by the aforementioned quote from Design Patterns.

Figure 2 shows an alternative Composite pattern implementation.

Figure 2. An alternative Composite pattern class diagram

If you implement Figure 2's Composite pattern, you don't ever have to distinguish between components and composites, and you don't have to cast a Component reference to a Composite instance. So the code fragment listed above reduces to a single line:

...
component.addComponent(someComponentThatCouldBeAComposite);
...

But, if the Component reference in the preceding code fragment does not refer to a Composite, what should the addComponent() do? That's a major point of contention with Figure 2's Composite pattern implementation. Because primitive components do not contain other components, adding a component to another component makes no sense, so the Component.addComponent() method can either fail silently or throw an exception. Typically, adding a component to another primitive component is considered an error, so throwing an exception is perhaps the best course of action.

So which Composite pattern implementation—the one in Figure 1 or the one in Figure 2—works best? That's always a topic of great debate among Composite pattern implementers; Design Patterns prefers the Figure 2 implementation because you never need to distinguish between components and containers, and you never need to perform a cast. Personally, I prefer Figure 1's implementation, because I have a strong aversion to implementing methods in a class that don't make sense for that object type.

Now that you understand the Composite pattern and how you can implement it, let's examine a Composite pattern example with the Apache Struts JavaServer Pages (JSP) framework.

The Composite pattern and Struts Tiles

The Apache Struts framework includes a JSP tag library, known as Tiles, that lets you compose a Webpage from multiple JSPs. Tiles is actually an implementation of the J2EE (Java 2 Platform, Enterprise Edition) CompositeView pattern, itself based on the Design Patterns Composite pattern. Before we discuss the Composite pattern's relevance to the Tiles tag library, let's first review the rationale for Tiles, and how you use it. If you're already familiar with Struts Tiles, you can skim the following sections and commence reading at "Use the Composite Pattern with Struts Tiles."

Websites often include multiple Webpages with identical layouts, such as Figure 3's sidebar/header/content/footer layout. Struts Tiles lets you reuse both content and layout among multiple Webpages. Before we discuss that reuse, let's see how Figure 3's layout is traditionally implemented with HTML alone.

The preceding JSP has two major drawbacks: First, the page's content is embedded in the JSP, so you cannot reuse any of it, even though the sidebar, header, and footer are likely to be the same across many Webpages. Second, the page's layout is also embedded in that JSP, so you likewise cannot reuse it even though many other Webpages in the same Website use the same layout. We can use the <jsp:include> action to remedy the first drawback, as I discuss next.

For completeness, I've listed below the JSPs included by the preceding JSP:

Example 4. header.jsp

<font size='6'>Welcome to Sabreware, Inc.</font>
<hr>

Example 5. content.jsp

<font size='4'>Page-specific content goes here</font>

Example 6. footer.jsp

<hr>
Thanks for stopping by!

Even though Example 2's JSP uses <jsp:include> to reuse content, you can't reuse the page's layout because it's hardcoded in that JSP. Struts Tiles lets you reuse both the content and the layout, as illustrated in the next section.

Example 7. Use Struts Tiles to encapsulate layout

The preceding JSP uses the <tiles:insert> tag to create Figure 3's JSP. That JSP is defined by a tiles definition named sidebar-header-footer-definition. That definition resides in the Tiles configuration file, which in this case is WEB-INF/tiles-defs.xml, listed in Example 8:

The preceding Tiles definition specifies the page layout, encapsulated in header-footer-sidebar-layout.jsp, and the page's content, encapsulated in sidebar.jsp, header.jsp, content.jsp, and footer.jsp, as listed in Examples 3-6. Example 9 lists the JSP that defines the layout—header-footer-sidebar-layout.jsp:

The preceding JSP encapsulates layout and inserts content according to the values specified for the sidebar, editor, content, and footer regions in the Tiles definition file, thus easing reuse for both the content and the layout. For example, you could define another Tiles definition with the same layout, the same sidebar, editor, and footer, but different content:

Thanks to Struts Tiles, you can reuse both content and layout, which proves invaluable for Websites with many JSPs that share layout and some content. But if you look closely at the code from Examples 7-9, you will notice that the layout for the sidebar region is hardcoded in sidebar.jsp, which is listed in Example 3. That means you cannot reuse that layout. Fortunately, the Tiles tag library implements the Composite pattern, which lets us specify a tiles definition—instead of a JSP—for a region. In the next section, I explain how to use that Composite pattern implementation.

Use the Composite pattern with Struts Tiles

Struts Tiles implements the Composite pattern, where the Component class is represented by JSPs and the Composite class is represented by a Tiles definition. That implementation lets you specify either a JSP (a component) or a Tiles definition (a composite) as the content for a JSP's region. Example 10 illustrates that feature:

The preceding Tiles configuration file defines two Tiles definitions: sidebar-definition and sidebar-header-footer-definition. The sidebar-definition is specified as the value for the sidebar region in the sidebar-header-footer-definition. You can specify it as such because Tiles implements the Composite pattern by letting Tiles specify a definition (a Composite that's a JSP collection) where you would normally specify a single JSP (which is a Component).

The sidebar's layout is encapsulated in Example 11's sidebar-layout.jsp:

Example 13. sidebar-links.jsp

Now the sidebar-definition can define other regions with a top and bottom component, although you should probably rename that definition to something more generic like top-bottom-definition.

It's all composites these days

The Composite pattern is popular with presentation frameworks, such as Swing and Struts, because it lets you nest containers by treating components and their containers exactly the same. Struts Tiles uses the Composite pattern to specify a simple JSP or a Tiles definition—which is a collection of JSPs—as a tile's content. That's a powerful capability that eases management of large Websites with different layouts.

The "Homework from Last Time" section below expands on this article's discussion by internationalizing the preceding application with a Struts action and the JSP Standard Tag Library (JSTL).

Homework

Discuss how Swing implements the composite pattern with the Component and Container classes.

Homework from last time

For this assignment, I decided to implement a Struts action in conjunction with the JSP Standard Tag Library (JSTL) to internationalize Example 1's Web application. Although Struts provides the necessary infrastructure to internationalize your Web applications, you should use JSTL for that task, because JSTL is a standard. At some point, the Struts internationalization capabilities will probably be deprecated or integrated with JSTL.

After I internationalized Example 1's Web application with a Struts action and JSTL, I localized that application for Chinese. Figure H1 illustrates the result.

Note: I don't know a single word of Chinese, let alone how to write the language, so Figure H1's Chinese is fabricated from arbitrary Unicode strings. But it looks cool, regardless.

Notice I specified the href attributes in Example 12's flags.jsp as empty strings, so when you click on the flags, the servlet container reloads the current JSP. Therefore, the first step towards internationalizing the Web application is to specify a URL for those attributes, as listed in Example H1:

The URL for each href attribute is the same: flags.do. Two request parameters are tacked onto that URL: one for the locale corresponding to the flag and another that represents the current JSP's path. You obtain the latter request parameter by invoking the Http.ServletRequest.getServletPath() method.

In the application's deployment descriptor, I mapped all URLs ending in .do to the Struts action servlet, like this:

Because of that mapping, the URL flags.do causes the Struts action servlet to invoke the actions.FlagAction.execute() method; therefore, clicking on a flag will invoke the actions.FlagAction.execute() method. Example H4 lists the actions.FlagAction class:

The actions.FlagAction.execute() method obtains a reference to the locale request parameter and passes that value to the JSTL Config class's set() method. That method stores the string representing the locale in the FMT_LOCALE configuration setting, which JSTL uses to localize text and format numbers, currencies, percents, and dates.

Now that I've specified a locale for JSTL internationalization actions, I next specify a resource bundle in the application's deployment descriptor, an excerpt of which is listed in Example H5:

The resource bundle basenameresources is specified for the javax.servlet.jsp.jstl.fmt.localizationContext context-initialization parameter. That means JSTL will search for a file named resources_en.properties when the locale is set to British English (en-GB) and resources_zh.properties when the locale is set to Chinese (zh-ZH).

Now that I've specified a resource bundle and a locale for the JSTL internationalization actions, I next modify the JSP files to use those actions, as Examples H6-H9 demonstrate:

Example H9. footer.jsp

Examples H6-H9's JSPs use the JSTL <fmt:message> action to extract Unicode strings from the resource bundle specified in the deployment descriptor. Examples H10 and H11 list the resource bundles for English and Chinese, respectively:

Email

"The enclosing object—known as a decorator—conforms to the interface of the object it encloses, allowing the decorator to be used as though it were an instance of the object it encloses."

However, the code example decorates the FileReader with a LineNumberReader and calls readLine(), which is not in the FileReader interface; thus you are not using the LineNumberReader transparently.

Both FileReader and LineNumberReader conform to the interface defined by the Reader class, which they both extend. The idea is that you can pass the decorator to any method that expects a reference to a Reader. Those methods remain blissfully ignorant of that decorator's special capabilities—in this case, the ability to read files and produce line numbers. However, if you know about those special capabilities, you can take advantage of them.

The fact that the decorator possesses (one or more) methods that the decorated object lacks does not in any way violate the Decorator pattern's intent; in fact, that's the central feature of that pattern: to add functionality at runtime to an object by decorating objects recursively.

If you look at Design Patterns page 173, you will see this:

Decorator subclasses are free to add operations for specific functionality. For example, ScrollDecorator's ScrollTo operation lets other objects scroll the interface *if* they know there happens to be a ScrollDecorator object in the interface.

David
Geary is the author of Core JSTL Mastering the JSP Standard Tag
Library, which will be published this fall by
Prentice-Hall and Sun Microsystems Press; Advanced JavaServer Pages (Prentice
Hall, 2001; ISBN: 0130307041); and the Graphic Java series (Sun Microsystems
Press). David has been developing object-oriented software with
numerous object-oriented languages for 18 years. Since the GOF
Design Patterns book was published in 1994, David has been
an active proponent of design patterns, and has used and
implemented design patterns in Smalltalk, C++, and Java. In 1997,
David began working full-time as an author and occasional speaker
and consultant. David is a member of the expert groups defining the
JSP standard custom tag library and JavaServer Faces, and is a
contributor to the Apache Struts JSP framework.