In a previous entry,
I discussed an annotation design pattern called "Annotation Inheritance".
Here is another annotation design pattern that I have found quite useful.
Class-Scoped Annotations
This design pattern is very interesting because it doesn’t have any
equivalent in the Java World.
Imagine that you are creating a class that contains a lot of methods with a
similar annotation. It could be @Test with
TestNG, @Remote if you are using
some kind of RMI tool, etc…
Adding these annotations to all your methods is not only tedious, it
decreases the readability of your code and it’s also quite error prone (it’s
very easy to create a new method and forget to add the annotation).
The idea is therefore to declare this annotation at the class level:
@Test
public class DataBaseTest {
public void verifyConnection() { … }
public void insertOneRecord() { … }
}
In this example, the tool will first look on each individual method if they
have an @Test annotation and if they don’t, look up the same annotation
on the declaring class. In the end, it will act as if @Test was
found on both on verifyConnection() and insertOneRecord().
The question now is: how will the tool determine which methods the
class annotation should apply to?
There are three strategies we can consider:
- Apply the annotations on all the methods (private, protected,
public).
Probably not the most intuitive way.
- Apply the annotations only on the public methods.
This seems fairly intuitive to me, you just need to be careful what
methods you declare public.
- Apply the annotations on a set of methods picked differently.
An interesting approach discussed further below.
Of course, we should also add another dimension to this matrix: should
the methods under consideration be only on the current class or also inherited
from superclasses? To keep things simple, I’ll assume the former for now,
but the latter brings some interesting possibilities as well, at the price of
complexity.
Using visibility as a means to select the methods might be seen as a hack, a
way to hijack a Java feature for a purpose different than what it was designed
for. Fair enough. Then how could we tell the tool which methods the
class-level annotation should apply to?
An antiquated way of doing it is using syntactical means: a regular
expression in the class-level annotation that identifies the names of the
methods it should apply to:
@Test(appliesToRegExp = "test.*")
public class DataBaseTest {
public void testConnection() { … } // will receive the @Test annotation
public void testInsert() { … } // ditto
public void delete() { … } // but not this one
}
The reason why I call this method "antiquated" is because that’s how we used
to do it in Java pre-JDK5. This approach has a few significant flaws:
- It forces you to obey a naming convention.
- It makes refactoring difficult (the IDE’s don’t know much about the
meaning of the string "test.*"). - It is not type safe (if the regular expression changes, you need to
remember to rename your methods).
A cleaner, more modern way to do this is to use annotations:
@Test(appliesToMethodsTaggedWith = Tagged.class)
public class DataBaseTest {
@Tagged
public void verifyConnection() { … }@Tagged
public void insertOneRecord() { … }
}
Of course, this solution is precisely what we wanted to avoid in the first
place: having to annotate each method separately, so it’s not buying us
much (it’s actually more convoluted than the very first approach we started
with).
So it looks like we’re back to square one: class-level annotations
applying to public methods seems to be the most useful and the most intuitive to
apply this pattern, and as a matter of fact, TestNG users have taken quite a
liking to it.
Can you think of a better way?