Pattern Summaries 6: Adapter and Decorator
DecoratorAs mentioned at the beginning of this article, the decorator pattern is structurally similar to the Adapter pattern but differs greatly in the intent.
Suppose you are responsible for maintaining software of a security system for controlling physical access to a building. Its basic architecture is that a card reader or other data-entry device captures some identifying information and passes that information to an object that controls a door. If the object that controls the door is satisfied with the information, it unlocks the door. Here is a collaboration diagram showing that:
Suppose you need to integrate this access control mechanism with a surveillance system. A surveillance system typically has more cameras connected to it than it has TV monitors. Most of the TV monitors will cycle through the images of different cameras. They show a picture from each camera for a few seconds and then move on to the next camera for which the monitor is responsible. There are rules about how the surveillance system is supposed to be set up to ensure its effectiveness. For this discussion, the relevant rules are:
- At least one camera covers each doorway connected to the access control system.
- Each monitor is responsible for not more than one camera that covers an access-controlled doorway. The reason is that if there are multiple cameras viewing a doorway, then the failure of a single monitor should not prevent the images from all of the cameras on that doorway being seen.
The specific integration requirement is that when an object that controls a door receives a request for the door to open, the monitors responsible for the cameras pointed at the doorway display that doorway. Your first thought about satisfying this requirement is that you will enhance a class or write some subclasses. Then you discover the relationships shown in this class diagram:
Figure 5. Security System Classes
There are three different kinds of doors installed and two different kinds of surveillance monitors in use. You could resolve the situation by writing two subclasses of each of the door controller classes, but you would rather not have to write six classes. Instead, you use the Decorator pattern, which solves the problem by delegation rather than inheritance.
What you do is write two new classes called DoorControllerA and DoorControllerB. These classes both implement the DoorControllerIF interface:
Figure 6. Door Controller Classes
The new class, AbstractDoorControllerWrapper, is an abstract class that implements all of the methods of the DoorController interface with implementations that simply call the corresponding method of another object that implements the DoorController interface. The DoorControllerA and DoorControllerB, are concrete wrapper classes. They extend the behavior of the requestOpen implementation that they inherit to also ask a surveillance monitor to display its view of that doorway. Here is a collaboration diagram showing this:
Figure 7. Door Surveillance Collaboration
This approach allows doorways viewed by multiple cameras to be handled by simply putting multiple wrappers in front of the DoorControllerIF object.
The following class diagram shows the general structure of the Decorator pattern:
Figure 8. Decorator Pattern
Below are descriptions of the roles that classes play in the Decorator pattern:
- An interface in this roll is implemented by all service objects that may potentially be extended through the Decorator pattern. Classes whose instances can be used to dynamically extend classes that implement the AbstractServiceIF interface must also implement the AbstractServiceIF interface.
- Classes in this role provide the basic functionality that is extended by the Decorator pattern. The Decorator pattern extends classes in this role by using objects that delegate to them.
- The abstract class in this roll is the common superclass for wrapper classes. Instances of this class take responsibility for maintaining a reference to the AbstractServiceIF object that ConcreteWrapper objects delegate to.
- This class also normally implements all methods declared by the AbstractServiceIF interface so they simply call the like-named method of the AbstractServiceIF object that the wrapper object delegates to. This default implementation provides exactly the behavior needed for methods whose behavior is not being extended.
- ConcreteWrapperA, ConcreteWrapperB, ...
- These concrete wrapper classes extend the behavior of the methods they inherit from the AbstractWrapper class in whatever way is needed.
We conclude this discussion of the Decorator pattern with a code example that implements some of the door controller classes shown in diagrams at the beginning of this discussion.
Here is an implementation of the DoorControllerIF interface:
Here is the AbstractDoorControllerWrapper class that provides default implementations to its subclasses for the methods declared by the DoorControllerIF interface:
Finally, here is a subclass of the AbstractDoorControllerWrapper class that extends the default behavior by asking a monitor to display the image from a named camera:
About the Author
Mark Grand is the author of a series of books titled "Patterns in Java." He is a consultant who specializes in object-oriented design and Java. He is currently working on a framework to create integrated enterprise applications.
Page 2 of 2