Preparing for AOP tide
What aspect-oriented programming means for today’s software developers.
Something truly fascinating is happening in the software industry. More and more people recognize the value and power of Aspect Oriented Programming (AOP). While mainstream tools are still few years away, it is important to understand what aspects mean in the context of regular OOP code and what concrete steps that can be taken to improve the quality of today’s design and code.
What is AOP?
I define aspect as a crosscutting property, pervasive feature or a general requirement of the system. Aspect can be applied to a system as a whole without having to specify all of the places where this requirement/property/feature has to be inhibited.
An aspect-oriented programming (AOP) is a programming style where a programmer defines aspects or chooses pre-defined aspects that are pertinent to the system. The aspects are then applied to the system as a whole, thus removing the need to sprinkle the system with code responsible for implementation of a particular aspect.
More information on AOP can be found here.
In practical terms, AOP is a great way to declare the kinds of things that see all over the code like logging, parameter checking, error porting. Things that programmers are sick of maintaining all over the place and wish would “just be there”.
Here, I’m looking at AOP from purely practitioner’s standpoint. It is not my intention to explain the theory, but to show the practical benefits AOP brings to today’s OO design and code.
AOP will be as widespread as OOP
The growth of attention towards AOP isn’t surprising, really. Ask any developer “Would you like to have magic tool that would take care of parameter checking so that you would never have to clutter your code with those validation checks anymore?” The answer will be mostly be “Yes” (maybe with some variations like “Hell, Yes!” or “I need it now!”). When people explain AOP, they use examples of aspects responsible for things like logging, security or error handling. Most developers I know understand the value proposition of AOP right from the first examples. This is because every experienced developer had spent countless hours on these very problems.
I think that within 3 years AOP will become mainstream style of programming. AOP will be integrated in high-quality development tools and available from many vendors.
Value of AOP today
So AOP’s transition from academia to mainstream is imminent. But while AOP support in development tools is a few years away, what is the value for today’s programmers? This new programming style offers some valuable guidelines directly applicable to the way programs are being developed today. These guidelines are directly applicable to the way we architect today’s software using today’s tools. They offer a powerful way to improve existing OO design and code.
Revenue and Overhead
A great book “About Face. The essentials of user interface design” by Alan Cooper contains a wonderful passage explaining the two different types of actions we take in our daily lives:
“When I want to drive to the office, I have to open the garage door, get in, start the motor, back out and close the garage door before I even begin the forward motion that will take me to my destination. All of these actions are in support of my automobile rather than in support of getting to my destination. if I had a metal-telephaty-matter-trasference module, I’d just picture my destination in my mind and then be there – no garages, no motor. My point is not to complain about the intricacies of driving, but rather to point out the difference between two types of actions we take to accomplish out daily tasks. Any large task, such as driving to the office, involves many smaller tasks. Some of these I call revenue tasks, work to solve the problem directly; these tasks like steering down the road toward my office.
Other tasks, which I call excise tasks, don’t contribute directly to solving the problem, but are necessary to accomplish it just the same.”
The reason I’m quoting Cooper’s book here is because the same observation is directly applicable to the way programmers go about achieving their tasks. Cooper’s term “excise task” isn’t directly applicable for the purposes of this discussion because you can’t really eliminate those kinds of tasks completely. Nether they represent something undesirable. More accurate would be to call these other tasks overhead tasks.
If I want to write a function that stores a string in a given file, I have to check to see if a file name that was proved is ok, create a new file or open an existing file. If all goes well, I’m now ready to do the business of writing the string into a file. Once I’m done, I’ll need to close the file and maybe return a success code to the caller. If there is some kind of problem, I need to figure out what the problem is and somehow throw it to the caller or report it. This example has only one revenue task – writing string in a file. This task is surrounded by a bunch of overhead tasks that we do just to make things work.
In order to tell overhead from revenue, one needs to be very clear about the goal of a component. Once the primary goal is clear, it is usually easy to tell what tasks are directed towards achieving this goal and what tasks are done to satisfy certain constraints of a component. For example, setup and dispose tasks are usually overhead tasks. Data transformations from one format to another are usually overhead tasks. Think of a last piece of code or design you were working on and try to classify which tasks are overhead and which are revenue. Overhead tasks are unavoidable, but it is a good design goal to keep them to a minimum.
Now that we know how to tell overhead from revenue tasks, let’s see how the two types of tasks live together in modern OO code. It is very common to see those overhead tasks are intertwined with revenue tasks. There is maybe one exception to this statement: structured exception handling. But OO languages offer little more to clearly separate revenue tasks from all of the other overhead tasks.
Overhead tasks have other curious properties. It if often required to perform a particular overhead tasks every time a certain revenue task is done. Here’s an example. Let’s say you have a system that sends data around between components. Now you need to log the event of data crossing component boundary for debugging purposes. The logging is overhead in this instance since log creation isn’t the primary goal of the system. So now revenue tasks of data transfer is bound with an overhead task of logging.
Another characteristic of an overhead task is its independence from revenue task. It is often possible to evolve them separately.
Overhead tasks as Aspects
Observe that the characteristics of aspects are very similar to those of overhead tasks. If a task is determined to be overhead, it is likely to be a good candidate to be declared as an aspect as well.
Once you draw the dividing line between revenue and overhead, the design comes into focus. The flow of the revenue tasks becomes much cleaner and unobstructed. Think of a difference between using structured exception handling versus dealing with errors at every place it may occur. The reason SEH-enabled code is easier to read and maintain is because its author got rid of one overhead task – error handling. Imagine the clarity of the code where all of other overhead tasks are separated from revenue tasks!
There are many examples of above principles. Clemens’ ASP.NET extensions are a good example of a design where overhead is separate from revenue. .NET Framework usage of attributes has many examples of removing overhead from the code. WebMethod is a big one. When creating a web service WebMethod attribute takes care of a number of tasks that unrelated to the goal of your web service such as creating a SOAP message.
How to prepare for AOP
In conclusion, there are certain things we can learn and use from AOP even if you’re not using AOP tools yet:
First, is to understand what is overhead-code and what is revenue-code in your program. Such understanding will make you think about the real goal of your system, component, class or function. Such understanding will also help you to see how overhead can be minimized.
Second, the quality of the existing code can be improved by separating overhead and revenue tasks using regular OO constructs such as classes, interfaces, libraries and functions. This way you can achieve independence between overhead and revenue code.
Third, use the tools, Java and C# attributes and code generation to implement aspects in OO languages. But this is a topic for a whole new discussion.
|