|
UML Diagram
Figure 3 shows the official UML diagram for the Decorator pattern

Figure 3: The official UML diagram for the Decorator design pattern.
Take a look at all the class diagrams.
- The Component class is any abstract class you want to decorate. Even before being decorated, this class has some defined behavior. In the Employee class, the defined behavior may deal with hire date, benefits, or other such things.
- The ConcreteComponent class is a concrete type derived from Component. The ConcreteComponent has some functionality dynamically added to it. For example, the PrincipleInvestigator class may have code to keep track of the investigator's research projects.
- The Decorator class is an abstract class, with two important features:
- The Decorator is derived from Component.
- The Decorator contains an instance of Component.
This may seem strange, but it's an important feature of the Decorator design pattern.
- The component variable refers to an instance of the Component class. Many Decorators may share this instance of Component.
For example, a variable may refer to a PrincipleInvestigator, and then be shared by the SafetyCaptain and BloodDriveCanvasser decorators.
- The ConcreteDecoratorA and ConcreteDecoratorB classes are concrete types inherited from Decorator. In the ongoing lab employee example, the concrete decorators SafetyCaptain, BloodDriveCanvasser, and ComputerSecurityOfficer can dynamically add additional functionality to a PrincipleInvestigator instance.
The abstract Decorator class is a subclass of Component but it also contains an instance of Component. It's like a robot in a science fiction moviea big, human-like form with an ordinary person sitting in a driver's seat somewhere inside. The driver has arms, legs, and a head, but the robot that contains the driver also has arms, legs, and a head. Now imagine that someone invents an even bigger robotone that's capable of having the first robot in its humongous driver's seat. Now you have the new human-like robot containing the original robot, which in turn contains the human driver. Everything in the system is human-like (with arms, legs, and a head) so everything in the chain can do the kinds of things the original human can do (like battle with evildoers or save innocent babies).
In the lab employee example, the SafetyCaptain can decorate an Employee. With a particular Employee instance in the driver's seat, the SafetyCaptain can do exactly what the original Employee can dohave a particular hire date, set of benefits, and other things. But being subclassed in some way from Employee, the SafetyCaptain can be further decorated by the BloodDriveCanvasser, making this employee both a SafetyCaptain and a BloodDriveCanvasser. Best of all, the decorating is done (and if necessary, undone) at runtime.
Using the Decorator
Here's a refactored design for the lab employee application:

Figure 4: The refactored UML diagram for the laboratory employee application.
The source code for this refactored diagram is shown in Listing 1
.
The following code is the client application. It starts by creating two principle investigator objects, investigator01 and investigator02 using the following statements:
Employee investigator01 = new PrincipleInvestigator();
Employee investigator02 = new PrincipleInvestigator();
Both principle investigators have the same basic role of "Analyze laboratory data" (see Listing 1). The first principle investigator, investigator01, is also a Safety Captain. To add that responsibility, you can use the existing investigator01 object and create a new SafetyCaptain object:
investigator01 = new SafetyCaptain(investigator01);
With this code, the SafetyCaptain object wraps itself around the PrincipleInvestigator object to dynamically change the principle investigator's behavior. Notice how it passes investigator01 into the SafetyCaptain class's constructor. Doing this, the SafetyCaptain (Listing 1) gets its own instance of the Employee object, and uses that instance to call the original PrincipleInvestigator's methods. Along with the original PrincipleInvestigator's methods, the new SafetyCaptain decorator adds its own functionality.
In Listing 1, the getResponsibility() method implemented in the SafetyCaptain class calls the getResponsibility() method that's implemented in its Employee instance, and then adds the "\n\tand perform quarterly safety inspections" text.
What about the second principle investigator? Like investigator01, this investigator is a safety captain. But investigator02 has also decided to be a blood drive canvasser. The following code adds this employee's two responsibilities:
investigator02 = new SafetyCaptain(investigator02);
investigator02 = new BloodDriveCanvasser(investigator02);
For investigator02, a BloodDriveCanvasser wraps around a SafetyCaptain which in turn wraps around a PrincipleInvestigator. In Listing 2
, we also create instances of ResearchTechnician and AdministrativeAssistant. Figure 5 shows the console output of the entire source code.
-- Research Laboratory --
Principle Investigator
Safety Captain
Responsibilities include Analyze laboratory data
and perform quarterly safety inspections
Principle Investigator
Safety Captain
Blood Drive Coorinator
Responsibilities include Analyze laboratory data
and perform quarterly safety inspections
and coordinate all blood drive activities
Research Technician
Computer Security Officer
Blood Drive Coorinator
Responsibilities include Acquire laboratory data
and handle computer security issues
and coordinate all blood drive activities
Administrative Assistant
Blood Drive Coorinator
Responsibility include Provide secretarial support
and coordinate all blood drive activities
Figure 5: The output of the laboratory employee application.
However, one of this stuff works unless you follow a very important design principle (quoted from the GoF book):
"Program to an interface, not an implementation."
Following this design principle keeps you from being tied to a specific implementation, and allows you to dynamically change behavior at runtime. If you program to an implementation you have:
PrincipleInvestigator investigator01 = new PrincipleInvestigator();
This code commits you to the PrincipleInvestigator class. In that case, a statement like this one:
investigator01 = new SafetyCaptain(investigator01);
from Listing 2 is illegal. You can't dynamically change the behavior of investigator01. If, instead, you program to the interface:
Employee investigator01 = new PrincipleInvestigator();
This allows you to dynamically modify behavior (in this case, add additional responsibilities for an employee) at runtime.
New on the Java Boutique:
New Review:
Time Management Made Easy with the Quartz Enterprise Job Scheduler
Why not just use the Java timer API? This open source scheduling
API boasts simplicity, ease-of-integration, a well-rounded feature
set, and it's free!
New Applet:
Reverse Complement
Reverse Complement is a simple applet that converts DNA or RNA
sequences into three useful formats.
Elsewhere on internet.com:
WebDeveloper Java
Lots of Java information on webdeveloper.com
WDVL Java
Thorough Java resource at the Web Developer's Virtual Library.
ScriptSearch Java
Hundreds of free Java code files to download.
jGuru: Your View of the Java Universe
Customizable portal with online training, FAQs, regular news updates, and tutorials.
|