Skip to main content

AOD and DRY

Posted by monika_krug on December 1, 2004 at 5:47 AM PST

Hello, I am new. My current main points of interest are:

  • Aspect Oriented Development (AOD), particularly AspectJ
  • which new features should or should not be added to Java 6.0 a.k.a. Mustang
  • UML
  • teaching Java or programming in general

so this is what I intend to blog about. I'll start with AOD.


What is AOD about?

Aspect Oriented Development is all about the DRY principle: Don't repeat yourself! Or, as it is usually put, it's about "separating crosscutting concerns".

Aspect Orientation adds a new kind of modularity to Object Orientation. Code that is repeated in many methods, which may be in one class or spread across many classes, and cannot be separated well into a method, can often be separated into an aspect.

Example: Handling null arguments

Imagine you have several methods (I'll just use two for the example) that build some kind of table from a Collection, maybe with different algorithms suited for different situations, or the results are different. In each of them you have the same handling for the case that the Collection that is passed as a parameter is null or has size 0. In the first version the unchecked CollectionIsEmptyException is thrown.

public class TableBuilder
{
  public static Table buildTable1(Collection coll)
  {
    if (coll == null || coll.size() == 0) throw new CollectionIsEmptyException();
    // now we can savely get the first element
    ElementType first = (ElementType) coll.iterator().next();
    // do something with first
    Table table = new Table(parameters);
    // go through entire collection and build table
    return table;
  }

  public static Table buildTable2(Collection coll)
  {
    if (coll == null || coll.size() == 0) throw new CollectionIsEmptyException();
    // now we can savely get the first element
    ElementType first = (ElementType) coll.iterator().next();
    // do something with first
    Table table = new Table(parameters);
    // go through entire collection and build table
    return table;
  }
}

The code for handling the empty Collection cannot be refactored into a method by itself, and it stands in the beginning of each of the "buildTable" methods, which is relevant for separating it into an aspect as we will see later.

Change

Now imagine you want to change this behavior for all of the methods (of course before you published the API). You want to return null instead of throwing the exception:
if (coll == null || coll.size() == 0) return null;
Now you have to change this in every spot where the original code exists. This clearly shows the disadvantage of copy-and-paste programming, a violation of the DRY principle. You might miss one of the occurences, especially if the concerned methods are spread across several classes.

Aspect-oriented solution

If the code for handling empty Collections had been modularized into an aspect in the first place, you would only have had to make the change once.

aspect Emptyness
{
  pointcut buildTable(Collection coll) :
    (call(Table TableBuilder.buildTable1(Collection)) ||
     call(Table TableBuilder.buildTable2(Collection))) &&
    args(coll);
 
  before(Collection coll) : buildTable(coll)
  {
    if (coll == null || coll.size() == 0)
      // old code: throw new CollectionIsEmptyException();
      return null;
  }
}

Isn't this so much nicer? Do not worry, the Aspect J Language is not hard to learn.

Wild Cards

It gets even better: Instead of having to write

call(Table TableBuilder.buildTable1(Collection)) ||
     call(Table TableBuilder.buildTable2(Collection))

which would become lengthy if we have lots of such methods, we can simply write
call(Table TableBuilder.buildTable*(Collection))
This has another marvelous benefit: If you later add a buildTable3 method to the TableBuilder class, you do not even have to think about adding this code to the method nor about adding the method signature to the pointcut in the advice - it is already covered.

What if you don't have TableBuilder.buildTable1() and TableBuilder.buildTable2(), but you have different classes? TableBuilder1.buildTable() and TableBuilder2.buildTable(). No problem:
call(Table pack.age.TableBuilder*.buildTable(Collection))


This is just a small example of what AOD, namely AspectJ, is useful for, it can do a lot more. I hope you will see it as useful, too.

See: http://eclipse.org/aspectj/

and especially The AspectJ Programming Guide

Related Topics >>