Skip to content
Chicago Tribune
PUBLISHED: | UPDATED:
Getting your Trinity Audio player ready...

Last month’s Hooked on Objects guided us through the processes of creating a class and compiling the code. This time we’ll introduce the concept of design patterns and explore the benefits they provide. Two very proven design patterns are the decorator and the proxy pattern. They both delegate message requests to an associated object to provide flexible solutions to common problems.

Object-oriented developers eventually discovered that they often used the same techniques when designing applications. They might not have realized it at the time, but they actually were using design patterns they had learned from hands-on development experience.

When discussing designs with each other, developers realized some design approaches proved successful in a variety of applications. A group of respected industry experts convened and agreed to formalize these best practices as “design patterns.” Instantly, new developers could learn the best practices that had been the result of years of application development experience.

Design patterns also provide a benefit to experienced developers. A common language for describing a design is important to facilitate communication among developers. If an alpine skier talks about doing a “daffy,” an airborne trick in which one leg goes forward while the other goes backward, other skiers understand precisely what is meant. There’s no need to describe the exact actions it took to perform the trick because a common language has been developed to describe the various body movements that a skier can do while jumping.

Although they’re a bit less exhilarating than hitting the slopes on a powder day, design patterns are a developer’s method to describe various design decisions, and knowledge of this terminology is vital to successful communication.

An introduction to the decorator pattern

“Design Patterns,” a book by a group known as the Gang of Four (see Resources), describes the Decorator pattern as a method of attaching: “additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.”

To better understand this definition, let’s review a working example of the decorator pattern. For our example we will extend the behavior of the MenuItem class. In the Java language class library, a MenuItem is a widget that represents the selectable menu options in an application’s user interface. A common menu such as “File” could have the menu items of “Open” and “Close.” Once a menu item is selected, an associated behavior is invoked and the object performs the function the user is expecting. The MenuItem class will be extended to track the number of times a menu item is selected.

Inheritance is a key element of an object-oriented development language.

Inheritance allows us to extend the functionality of an object by providing new methods and specifying that the new object inherit all existing behavior of the original object. Inheritance is available to us, so why would we use the decorator pattern? Could we accomplish the same thing by subclassing the class we wanted to extend? The intent of the decorator pattern is to provide more flexibility than inheritance.

A new class called TrackingMenuItem extends the original MenuItem.

public class TrackingMenuItem extends MenuItem

(open brace symbol)

public int getClickCount()(open brace symbol)(closed brace symbol)

(closed brace symbol)

The class contains all of the behavior of the MenuItem, and it extends the behavior by adding the method getClickCount.

The Decorator pattern would accomplish virtually the same thing through an association and delegation.

public class TrackingMenuItem

(open brace symbol)

MenuItem theDelegate;

public TrackingMenuItem(MenuItem itm)

(open brace symbol)

theDelegate = itm;

(closed brace symbol)

public int getClickCount()(open brace symbol)(closed brace symbol)

/* delegate all other method signatures */

public void aMethodOnTheDelegate()

(open brace symbol)

theDelegate.aMethodOnTheDelegate();

(closed brace symbol)

//repeat for all methods

(closed brace symbol)

The decorator pattern appears to involve a lot more effort than the inheritance approach, so why would we use such an approach?

The flexibility within the decorator pattern rewards our effort. Suppose we wanted the getClickCount behavior of a menu item to apply to the MenuItem class (as in our example) and all subclasses of the MenuItem. To accomplish this using inheritance would require the creation of a new class for every subclass of MenuItem. Each would have the exact same code as the other. The only difference would be the class from which it extends. Depending upon the number of subclasses of MenuItem this could result in many new objects all doing the same thing.

The decorator pattern provides the flexibility to solve this challenge efficiently because all subclasses of MenuItem are instantly supported. Every class that extends from MenuItem can be decorated with our TrackingMenuItem to keep track of the number of times a particular menu item is selected. Of course, if I needed a behavior of a specific subclass for a MenuItem, the TrackingMenuItem would not have that behavior. Your application requirements will tell you which approach is appropriate.

The proxy pattern

The proxy pattern also uses delegation in its implementation, but the intent is very different from the decorator pattern. According to the Group of Four book, the proxy pattern’s purpose is to “provide a surrogate or placeholder for another object to control access to it.” A proxy often is used when developers wish to control, intercept or manage access to a particular object. The proxy pattern often is selected for use within a distributed computing environment.

For this example, assume we have an interface called Document. An interface in Java defines the behavior that can be expected of all classes that use this interface.

public interface Document

(open brace symbol)

public int getDocumentSize(User currentUser);

(closed brace symbol)

An interface contains no implementation code, so it often requires the creation of at least one implementation of that interface.

public class DocumentImpl implements Document

(open brace symbol)

public int getDocumentSize(User currentUser)

(open brace symbol) return 109; (closed brace symbol)

(closed brace symbol)

This implementation is pretty simple. Another implementation of the Document interface is our proxy. The proxy will intercept and control access to the original implementation.

public class ProxyDocument implements Document

(open brace symbol)

Document targetDocument;

User badUser;

public ProxyDocument(Document doc, User

userToPreventAccess)

(open brace symbol)

targetDocument = doc;

badUser = userToPreventAccess;

(closed brace symbol)

public int getDocumentSize(User User currentUser)

(open brace symbol)

if(user.equals(badUser))

(open brace symbol)

return 0;

(closed brace symbol)

else

(open brace symbol)

return targetDocument.getDocumentSize(currentUser);

(closed brace symbol)

(closed brace symbol)

(closed brace symbol)

When creating the proxy, the code must provide both the target document and a user object. When a request for the document size is made, the proxy validates the user by checking it against the known badUser. If the user is not someone the proxy is trying to block, it passes the request to the target document. While the above example is contrived, it does illustrate a usage of the proxy pattern. The proxy is used to control access to the original target object.

The decorator pattern and the proxy pattern are very similar in their implementations. However, it is important to understand the subtle differences between the patterns. Each contains a reference to another object. The method signatures of the original or delegate object are duplicated in the proxy or the decorator. The key difference is the intent of the pattern. The proxy pattern is intended to intercept and manage access to the target, while the decorator pattern intends to enhance and extend the target. The implementation of one pattern over the other might not vary greatly, but the intent is very different.

If you are new to OO development, we hope you can see how the previous design patterns can help you. Experienced developers probably recognized the two approaches and didn’t realize that they were actually using design patterns. An understanding of design patterns will help both new and experienced developers by providing a common language for communication and as a source of inspiration for your own designs.

———-

David Hoag is vice president-development and chief object guru for ObjectWave, a Chicago-based object-oriented software engineering firm. Anthony Sintes is a Sun Certified Java Developer and team memberspecializing in telecommunications consulting for ObjectWave. Contact them at hob@objectwave.com or visit their Web site at www.objectwave.com.

RESOURCES

“Design Patterns,” Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1995, Addison-Wesley Professional Computing, $49.95)

Hooked on Objects archive:

chicagotribune.com/tech/developer/archives/ HOBarchive