Aop@Work:: Enhance Design Patterns With Aspectj, Part 1
Aop@Work:: Enhance Design Patterns With Aspectj, Part 1
Country/region [select]
Terms of use
All of dW
Home
Products
developerWorks
My account
Contents:
AOP for patterns
The Adapter pattern
A Java-language Adapter
An AspectJ Adapter
The Decorator pattern
Download
About the author
Rate this article
Related content:
Improve modularity with aspect-oriented programming
Develop aspect-oriented Java applications with Eclipse and AJDT
AOP@Work series page
Enhance design patterns with AspectJ, Part 2
What is a design pattern? According to Design Patterns: Elements of Reusable ObjectOriented Software (commonly referred to as GoF; see Resources for details):
Subscriptions:
dW newsletters
A design pattern systematically names, motivates, and explains a general design that addresses a
recurring design problem in object-oriented systems. It describes the problem, the solution, when to apply
the solution, and its consequences. It also gives implementation hints and examples. The solution is a
general arrangement of objects and classes that solve the problem. The solution is customized and
implemented to solve the problem in a particular context.
After years of successfully applying patterns to solve problems in OO systems, I found myself nodding along with this
definition. Patterns were a great way to talk to my fellow programmers about design, and they represented best practices
that addressed recurring design problems. So it was a bit of a shock to me when I attended a talk by Stuart Halloway where
he suggested an alternate title for the GoF: "Workarounds for things that are broken in C++." His point was that what exists
as a "pattern" in one language can be subsumed into the language itself in a different paradigm. He went on to give the
example of Factories -- useful in the Java language, but less useful in Objective-C, which lets you return subtypes from a
constructor.
I had to think about it for a while before I realized that the two sides were saying the same thing: design patterns give us a
vocabulary for expressing concepts that can't be said directly in the programming language.
So where does AOP come in? For OOP, we have the GoF patterns, which give us a consistent, though sometimes
cumbersome, way of working with common concepts like observers and decorators. AOP builds on OOP to give us a direct
way of expressing crosscutting concerns. It turns out that some of the GoF patterns are about crosscutting and can be
expressed directly in AOP. So what you'll notice is that some of the patterns that involve many classes can be expressed
with a single aspect. Some patterns become easier to use because they involve less code. Some are so well supported that
they almost disappear. Others are strictly tied to OOP (for example, the patterns dealing with class structures) and remain
unchanged when used in conjunction with AOP.
This article explores pattern implementation with AOP (specifically AspectJ). I chose
to tackle GoF patterns because of their widespread popularity and general utility. In
this part of the article, I set up some criteria for analyzing the impact of patterns and
then go on to investigate the Adapter and Decorator patterns. Adapter demonstrates
the benefits of static crosscutting, while Decorator reveals itself as a disappearing
pattern. In Part 2, I offer a more in-depth study of the Observer pattern, which does
not disappear, but sees major benefits when implemented in AspectJ. Part 2
illustrates how AspectJ allows patterns to be converted into reusable base aspects,
thus allowing you to download prebuilt pattern libraries -- an exciting prospect for the
pattern enthusiast.
What is crosscutting?
Programs often exhibit behavior that does
not fit naturally into a single program
module, or even several closely related
program modules. The aspect community
describes this type of behavior as
crosscutting because it cuts across the
typical divisions of responsibility in a given
programming model. In OO programming,
for instance, the natural unit of modularity
is the class, and a crosscutting concern is
a concern that spans multiple classes.
Thus, if a design pattern contributes
behavior to three otherwise distinct
classes, that design pattern can be said to
crosscut those classes. Crosscutting leads
to code scattering (related code does not
localize with other related code) and code
tangling (related code sits next to unrelated
code). This scattering and tangling makes
it difficult to reason about the system. For a
Click the Code icon at the top or bottom of this article (or see Download) to download
the complete source for upcoming examples.
Some of these sensor classes were written by other team members, some by third-party vendors. What you would like to
do is to provide a display of each sensor's status, so that a mission commander can see at a glance whether his craft is in
trouble. Here's an example of what you're looking for. (The real display would probably involve flashing red lights and
klaxons, but I'll stick to text for now.)
Readout:
Sensor 1
Sensor 2
Sensor 3
Sensor 4
status
status
status
status
is
is
is
is
OK
OK
BORDERLINE
DANGER!
You could accomplish a display like this using the following method:
So far, so good, but how do you read each sensor without resorting to ugly if(sensor instanceof XXX) checks? One
option is to modify each sensor class to have a getStatus() method that interprets the sensor's readings and returns a
String, as shown here:
Doing this introduces unrelated status-display code into multiple classes, adding to their complexity. Further, there might be
practical limitations (such as having to recompile a third-party class). That's where the Adapter pattern steps in.
A Java-language Adapter
The traditional implementation of the Adapter pattern works by wrapping each target class with a class that implements a
convenient API. In this case, you would create a common interface called, say, StatusSensor, shown here:
With this common interface in place, you could implement the readout method like this:
The only remaining challenge is to make each sensor conform to the interface. The Adapter classes accomplish this. As
you can see in Listing 1, each Adapter stores the sensor it wraps in a member variable and uses that underlying sensor to
implement the getStatus method:
Listing 1. Adapter classes and client code
//Adapter classes
public class RadiationAdapter implements StatusSensor {
private final RadiationDetector underlying;
public RadiationAdapter(RadiationDetector radiationDetector) {
this.underlying = radiationDetector;
}
public String getStatus() {
if(underlying.getCurrentRadiationLevel() > 1.5){
return "DANGER";
}
return "OK";
}
}
public class TemperatureAdapter implements StatusSensor {
//...similar
}
//glue code to wrap each sensor with its adapter...
allSensors.add(new RadiationAdapter(radiationDetector));
allSensors.add(new TemperatureAdapter(gauge));
The listing also shows "glue code" that wraps each sensor with the appropriate Adapter before the readout. The pattern
does not dictate that this glue code appear in any particular place. Likely locations include "just after creation" and "just
before use." The sample code places it just before it adds the sensor to the readout collection.
Understanding: Evocatively named SensorAdapters co-located in a package make the intent of this pattern clear.
Unfortunately, the glue code's location might be far from the Adapter package. Since the glue code area is
unstructured, you might overlook it while attempting to understand the pattern or trip over it while trying to
understand the code it tangles with.
You must also take care to deal with issues of object identity. That is, if wrapped and unwrapped versions of the
same object coexist in a system, you will have to sort out whether they should be regarded as equal.
An AspectJ Adapter
As with other design patterns, the AspectJ implementation of Adapter preserves the intent and concepts of its cousin. The
implementation uses intertype declarations, an important type of crosscutting support that gets less airtime than pointcuts
and advice. If you need a refresher on static crosscutting, check out the Resources section for appropriate pointers.
As with the pure OOP version, the AOP version of Adapter requires the StatusSensor interface. However, instead of using
separate wrapper classes, the AspectJ version uses the declare parents form to make the various sensors implement
http://www-128.ibm.com/developerworks/library/j-aopwork5/ (5 of 14)5/19/2005 5:48:03 AM
Now the sensors should conform to the interface. But they do not yet implement the interface (a fact that the AspectJ
compiler will happily point out to you). To complete the pattern implementation, you must add intertype method declarations
to the aspect to make each sensor conform. The code below adds the getStatus() method to the TemperatureGauge
class:
The AspectJ version of the readout class looks the same as the version implemented in the Java language, except that no
glue code is necessary to wrap the sensors. Each sensor is its own wrapper.
The upshot is that both the Java and AspectJ implementations do a good job of
staying out of the way of the sensor classes. However, only the AspectJ version stays
out of the rest of your application. Is this a major advantage? It probably depends on
whether your application exhibits any of the complicating properties described in the
analyses. If I were using AspectJ on a project, I would definitely use it to implement
Adapter, although I wouldn't introduce AspectJ just to solve this problem. The next
pattern, Decorator, provides some more compelling advantages.
You may wish to have the javadocs or indeed the source code for java.io and ProgressMonitorInputStream open
beside you as you consider the next section; see Resources for further reference.
behavior. In java.io, FilterInputStream provides this functionality. Finally, a ConcreteDecorator extends the
AbstractDecorator, overrides methods requiring decoration, and adds behavior before or after invoking the same method
on the decorated component. In this case, ProgressMonitorInputStream plays the ConcreteDecorator.
Glancing at Sun's implementation of ProgressMonitorInputStream (which I won't reprint here because of licensing
concerns), you can see that it instantiates a javax.swing.ProgressMonitor upon creation. After every read method, it
updates the monitor with a count of how many bytes have been read from the underlying stream. The separate
ProgressMonitor class determines when to pop up a monitoring dialog and updates the visual display.
To use ProgressMonitorInputStream, you simply need to wrap another input stream (as in Listing 2) and be sure to refer
to the wrapped instance when doing reads. Notice the similarity here between the Adapter and Decorator patterns: both
require programmatic application of the additional behavior to the target class.
Listing 2. Monitoring an InputStream
private void actuallyReadFile() {
try {
InputStream in = createInputStream();
byte[] b =new byte[1000];
while (in.read(b) != -1) {
//do whatever here
bytesRead+=1000;
}
bytesReadLabel.setText("Read " + (bytesRead/1000) + "k");
bytesRead = 0;
in.close();
} catch (Exception e) {
//handle...
}
}
Understanding: Once you know Decorator is at work, it's fairly easy to comprehend. But I'll never forget the
confusion I felt the first time I cracked open java.io and tried to make sense of the wealth of machinery classes
that made up Decorator as applied to streams. Although a quick tutorial could have easily set me straight, there
was no easy way to arrive at a comprehension of the pattern just from looking at the code. A more concrete
measure of comprehension burden is lines of code. I'll take a look at line counts after examining the AspectJ
implementation. It's also worth noting that, because it uses wrapping, Decorator suffers from the same object
identity issues that affect Adapter.
An AspectJ Decorator
In their paper, Hanneman and Kiczales state that
Using AspectJ, the implementation of some patterns completely disappears, because AspectJ language
constructs implement them directly. This applies to [Decorator].
Taking a look at the Motivation section for Decorator in the GoF book, it becomes obvious why this would be the case:
The decorator forwards requests to the component and may perform additional actions (such as drawing a
border) before or after forwarding. Transparency lets you nest decorators recursively, thereby allowing an
unlimited number of additional responsibilities.
What is advice after all, but the ability to transparently add additional "actions" to any operation? In a sense, AspectJ lets
you decorate any method. To see how this plays out in a real system, you can examine the monitoring of input stream
reads in AspectJ.
pointcut arrayRead() :
call(public int InputStream+.read(..));
Now you can apply some advice of the following general form:
This advice uses the returning form to expose the return value of the method call. The number of bytes read is then fed to
a private method on the aspect: updateMonitor(). This method takes care of the details of updating the actual
ProgressMonitor (more on this later).
implementation:
To obtain the GUI component it needs, the aspect must bind it in the pointcut so that it can be used by the advice. Listing 3
contains the revised pointcut and advice. Notice that the fromAComponent() pointcut makes use of the primitive cflow()
pointcut. In essence, the pointcut is saying "select all join points that occur as a result of the execution of a method on a
JComponent and expose that component for use in advice."
Listing 3. Ferrying context to the monitor using cflow
pointcut arrayRead(JComponent component, InputStream is) :
call(public int InputStream+.read(..)) && target(is)
&& fromAComponent(component);
pointcut fromAComponent(JComponent component) :
cflow(execution(* javax.swing.JComponent+.*(..))
&& this(component));
after(JComponent component, InputStream is) returning (int bytesRead) :
arrayRead(component, is)
{
updateMonitor(component, is, bytesRead);
}
Maintaining state
To make the aspect widely applicable (and to accurately mimic the other implementation), the aspect must maintain state.
That is, it should pop up a unique progress monitor per monitored stream. AspectJ offers several options for dealing with
this. The best choice for this aspect is probably to maintain per-object state storage using a Map. This technique will
reappear in my implementation of the Observer pattern, so take note! (Other ways to store state specific to an object
include intertype declarations and pertarget/perthis aspects, but considering these concepts is beyond the scope of this
article.)
To implement state storage, you first declare a WeakHashMap that takes a stream as a key and stores a monitor as a value.
You want to use a WeakHashMap because WeakHashMaps will not prevent their keys from being garbage collected if the keys
are no longer in ordinary use. This best-practice prevents the aspect from holding references to defunct objects and thereby
creating a memory-leak.
The updateMonitor() method then uses the map to lazily initialize a new IncrementMonitor. Once the method is sure the
monitor exists, it updates it with the latest progress (indicated by the return value of read()). Listing 4 shows the code for
the lazy initialization and progress updates, as well as the full code for IncrementMonitor:
Listing 4. Lazy initialization of per-stream monitor
Finally, the aspect needs to discard the monitor when the stream has been fully read. If you're thinking in aspects at the
moment, you'll recognize the opportunity. InputStream conveniently defines a close() method for the aspect to advise, as
shown here:
before(InputStream is):
call(public void InputStream+.close())
&& target(is)
{
System.out.println("Discarding monitor.");
perStreamMonitor.remove(is);
}
At this point, you've completed the exercise. If you're familiar with the implementation of InputStream, however, you'll have
spotted something I deliberately left out. The read() method (no parameters) must be handled differently from the other
readmethods because its return value is not the number of bytes read, but rather the next byte in the stream. The example
code that accompanies this article expands the aspect to deal with this constraint, but I urge you to visualize how you would
address this concern if you were writing the aspect before referring to the code.
Understanding: Because of the power of AspectJ's pointcut language, an aspect can affect multiple operations
with the same advice. In contrast, a decorator class must repeat the behavior at each operation. In part because of
this, Sun's implementation shows more than twice as many lines of code as the aspect implementation. I counted
approximately 110 lines for the ProgressMonitorInputStream and 40 for FilterInputStream (I'll leave out
InputStream since it could be a legitimate superclass absent the Decorator pattern). In contrast, the
MonitorFileReads aspect consumes 53 lines, and the IncrementMonitor helper class consumes 12. The line
ratio stands at 160 to 65, or about 2.4 to 1. Although lines-of-code (LOC) is a crude measure, in general shorter
code is clearer code.
Furthermore, if you are familiar with AOP, the AspectJ solution does not give you the sense that something special
is going on. The Java language solution requires several classes working carefully in concert, while the AspectJ
version looks as if it's doing what most aspects do: adding behavior to a set of join points through advice.
Finally, it's worth remembering that a frequent criticism of AOP is that you can "no longer tell what a module is
doing by reading the source." If you apply decorators to objects without the help of aspects, there's no sourcebased clue in either the client code (other than the wrapping location) or the decoration target (the
FileInputStream) that the object displays additional behavior. In contrast, if you examine the GUI from Listing 2 in
AJDT, you will see a friendly annotation on the line while (in.read(b) != -1) that indicates that the monitoring
aspect affects the read call. The combination of AspectJ and its development environment provides better
information in this case than the original implementation.
Reusing: Because decoration is built into the language, almost all aspects reuse this pattern. A more specific
reuse would be to make the monitoring aspect abstract and allow subaspects to specify a pointcut for monitored
operations. In this way, nearly any object could be decorated with monitoring -- without the preparation required by
the traditional implementation. (If you're wondering about abstract aspects, Part 2 of the article explains their use in
more detail.)
Maintaining: Adding a new decoration to an object requires no special effort. If the decoration target changes
(imagine a new type of read method), then you must (possibly) update the pointcuts to account for this. Having to
update a pointcut is burdensome, but the burden can be reduced by writing robust pointcuts that are likely to catch
new operations. (See the Resources for a link to a great blog entry on robust pointcuts.) In any case, updating a
pointcut seems less trouble than updating all the decorators as would be required for a similar change in the Javalanguage implementation.
Here's another interesting scenario (mentioned earlier in the Java language analysis): monitoring all file reads. With
an OO decorator, this means that every class that reads a stream must remember to wrap it in a
ProgressMonitorInputStream. In contrast, the MonitorFileReads aspect will monitor reads on any input stream
as long as they occur from within the control flow of a JComponent. Because ProgressMonitor only ever pops up
when the operation is taking longer than a preset threshold, this aspect could transparently ensure that users never
get annoyed at having to wait for a file read -- without the need for programmer vigilance.
Composing: Like the competing implementation, the AspectJ version allows for transparent composition of
multiple decorators with low effort.
As I've mentioned before, Decorator's chief trick (transparently adding behavior to an operation) is subsumed by the
AspectJ language. The only challenge for the AspectJ implementation is how to associate aspectual state (the updated
progress monitor) with a specific instance -- the example used a map to make this association. This need to handle the
association preserves Decorator as a pattern in AspectJ. Sometimes, when the decoration machinery already exists, it
seems easier to use a traditional Decorator -- especially because the pattern does not invade the decorated class.
However, if the decoration machinery does not exist, the flexibility and simplicity of the AspectJ implementation make a
better choice.
Conclusion to Part 1
I hope that this tour of two familiar patterns has helped illustrate aspect-oriented mechanisms in practice. As the
development community grapples with the ramifications of an emerging paradigm, it can be useful to apply the new
technology to old problems -- problems for which good solutions already exist. The exercise can provide a familiar vantage
from which to asses the new approach.
So how has it fared so far? While it's not a golden hammer, AspectJ has managed to secure some solid advantages when
used to implement traditional OO patterns. These advantages stem from AspectJ's ability to better handle crosscutting
concerns. By gathering the code for a pattern into a single aspect, AspectJ makes it easier to understand the pattern from
reading the code. Because pattern code does not show up in non-pattern classes (such as the wrapping locations required
http://www-128.ibm.com/developerworks/library/j-aopwork5/ (12 of 14)5/19/2005 5:48:03 AM
by Adapter and Decorator) these other classes are also easier to understand. The combination also makes it easier to
extend and maintain the system, and even to reuse the patterns elsewhere.
Adapter and Decorator represent medium-complexity patterns. In Part 2 of this article, I'll examine whether aspectorientation scales to more complex patterns. Specifically, Part 2 tackles Observer, a pattern that involves multiple roles and
dynamic relationships. Part 2 also explores aspect-oriented reuse -- the ability to define a pattern or protocol as an abstract
aspect and to apply it with an application-specific aspect.
Resources
Click the Code icon at the top or bottom of this article (or see Download) to download the source code discussed in
this article.
AOP@Work is a year-long series dedicated to helping you incorporate AOP into your day-to-day Java
programming. Don't miss a single article in the series. See the complete series listing.
If you need an introduction to AspectJ and AOP, check out the introductory article "Improve modularity with aspectoriented programming" (developerWorks, January 2002).
For more information on the AspectJ development environment (AJDT), take a look at and this offering from
members of the AJDT team: " Develop aspect-oriented Java applications with Eclipse and AJDT" (developerWorks,
September 2004).
The section on Decorator suggests that you take a look at the sources for java.io. Sun provides an
implementation: J2SE 1.4.2.
Learn more about the Gang of Four and their book at Hillside.net.
The Aspect-Oriented Design Pattern Implementations project has so far refactored 23 GoF patterns using AspectJ;
three of them are studied in this article.
Project authors Jan Hannemann and Gregor Kiczales have made reusable abstract aspects from 13 of the studied
patterns freely available under the Mozilla Public License.
For a more in-depth study, see Hannemann and Kiczales's "Design Pattern Implementation in Java
AspectJ" (OOPSLA, November 2002), which proposes that AspectJ implementations of the GoF design patterns
show modularity improvements in 17 of 23 cases.
Adrian Colyer's The Aspects Blog brings you up to the minute news from the AspectJ project combined with
penetrating design insights and practical applications of aspect technology. This entry discusses how to write
robust pointcut expressions .
The Portland Pattern Repository is an excellent resource for learning about patterns, as well as a great introduction
to the patterns community. It includes sections on the Adapter, Decorator, and Observer patterns.
Ramnivas Laddad's AspectJ in Action: Practical Aspect-Oriented Programming (Manning, 2003) includes several
forward-thinking patterns for AOP.
You may also want to check out these excellent titles written by contributors to the AOP@Work series:
Mastering AspectJ (Joseph D. Gradecki and Nicholas Lesiecki; John Wiley & Sons, 2003).
Eclipse AspectJ: Aspect-Oriented Programming with AspectJ and the Eclipse AspectJ Development Tools
(Adrian Colyer, Andy Clement, George Harley, Matthew Webster; Addison-Wesley Professional, 2004).
You'll find articles about every aspect of Java programming in the developerWorks Java technology zone.
Visit the Developer Bookstore for a comprehensive Listing of technical books, including hundreds of Java- related
titles.
Also see the Java technology zone tutorials page for a complete Listing of free Java-focused tutorials from
developerWorks.
Download
Name
Size
Download method
j-aopwork56code.zip
142 KB
FTP
Disagree (2)
Neutral (3)
Comments?
Submit feedback
developerWorks
About IBM
Privacy
Contact
Agree (4)