Monday, October 09, 2006

AOP : Writing Expressive Pointcuts

Aha .. yet another rant on AOP and pointcuts, this time expressing some of the concerns with the most important aspect of aspects - the Pointcut Descriptors. In order for aspects to be a first class citizen of the domain modeling community, pointcut descriptors will have to be much more expressive than what they are today in AspectJ. Taking an example from one of the threads in "aspectj-users" forum, AOP expert Dean Wampler himself had made a mistake between call( @MyAnnotation *.new(..) ) and call( (@MyAnnotation *).new(..) ), while answering a query from another user. While the former pointcut matches all constructors annotated with @MyAnnotation, the latter matches constructors in classes where the class itself has the same annotation.

This is, at best, confusing - the syntax is not expressive and liberal sprinkling of position dependent wild card characters pose a real challenge to the beginners of AspectJ. Dean has some suggestions in his blog for making pointcut languages more expressive - as Dean has pointed out, the solution is to move towards a flexible DSL for writing pointcuts in AspectJ.

What we write today as :


execution(public !static * *(..))


can be expressed more effectively as :


execution(
  method()
  .access($public)
  .specifier(!$static)
)



The experts need to work out the complete DSL to make life easier for the beginners.


Pointcuts can be Intrusive

If not properly designed, pointcuts can directly bite into the implementation of abstractions. Consider the following example from the classic An Overview of AspectJ paper by Kiczales et. al. :


interface FigureElement {
  void incrXY(int x, int y);
}

class Point implements FigureElement {
  int x, y;
  // ...
}



Now consider the following two pointcuts :


get(int Point.x) || get(int Point.y)


and


get(* Shape+.*)


Both the above pointcuts match the same set of join points. But the first one directly intrudes into the implementation of the abstraction accessing the private fields, while the latter is based only on the interface. While both of them will have the same effect in the current implementation, but certainly, the first one violates the principle of "programming to the interface" and hence is not modular and scalable. While pointcuts have the raw power to cut into any levels of abstraction and inject advice transparently, care should be taken to make these pointcuts honor the ageold abstraction principles of the object oriented paradigm. As Bertrand Meyer has noted about OO contracts, pointcuts should also be pushed up the inheritance hierarchy in order to ensure maximal reusability.

Jonas Boner, while talking about invasive pointcuts has expressed this succinctly in his blog :
A pointcut can be seen as an implicit contract between the target code and the artifact that is using the pointcut (could be an aspect or an interceptor).
One problem with using patterns like this is that we are basing the implicit contract on implementation details, details are likely to change during the lifetime of the application. This is becoming an even bigger problem with the popularity of agile software development methodologies (like XP, Scrum, TDD etc.), with a high focus on refactoring and responsiveness to customer ever-changing requirements.


Metadata for Expressiveness

Ramnivas Laddad has talked about metadata as a multidimensional signature and has described annotations as a vehicle to prevent signature tangling and express any data associated with your code's crosscutting concerns. While annotations make code much more readable, but it is a compromise on one of the most professed principles of AOP - obliviousness. Annotations (and other mechanisms) can also be used to constrain advice execution on classes and interfaces. There have also been suggestions to have classes and interfaces explicitly restrict aspects or publish pointcuts. All of these, while publishing much more powerful interfaces for abstractions, will inherently limit the obliviousness property of AOP. See here for more details.

Use metadata to enhance the artifact being annotated, but the enhancement should be horizontal and NOT orthogonal. e.g. a domain model should always be annotated with domain level metadata and, as Jonas has rightly pointed out, it is equally important to use the Ubiquitous Language for annotating domain artifacts. Taking cue from the example Ramnivas has cited :


@Transactional
@Authorized
public void credit(float amount);



If the method credit() belongs to the domain model, it should never be annotated with service level annotations like @Transactional and @Authorized. These annotations go into service layer abstractions - the domain layer can contain only domain level metadata. Accordingly the pointcut processing for domain layers should not contain service layer functionality.

No comments: