BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Interviews AOP Refactoring with Ramnivas Laddad

AOP Refactoring with Ramnivas Laddad

Bookmarks
   

1. Ramnivas can you please introduce yourself and tell us what you're up to?

My name is Ramnivas Ladddad and I'm the author of "AspectJ In Action", I specialize in the subject of Aspect Oriented Programming and AspectJ. As my job I'm an independent consultant providing training and consulting in J2EE and Aspect Oriented Programming.

   

2. How would one go about adopting AOP?

My general solution is to do it in a gradual manner, essentially "don't run before you can walk". AOP is a new technology so you have to understand the style of thinking about AOP. Some of these things come only from experience, there's no real substitute for it. You can read a book or some articles but ultimately you need to gain some experience. I suggest starting with developmental aspects that means logging and tracing. This is done not for the audit tracing purpose, but just to have some debuggability into your program. Let's say you're running into some kind of concurrency control problem. You can add some logging and do those kinds of things.

The second kind of developmental aspects are performance monitoring aspects; you can add things like testing aspects. These testing aspects tend to be like "what happens if I don't encounter the happy path, where things are just going fine, where the network is up and running? What happens if things go wrong?" Do you respond correctly? Let's say you want to test that thing. How are actually going to get network down? You can introduce aspects and those aspects can inject faults and then you can test against those faults. These aspects are policy enforcement aspect; you may have some high level architecture policies; let's say database access should be only through DAOs, nobody in JSP page, nobody in view should go directly to the database.

How are you going to enforce that? You can write simple aspects; that's like extending you a compiler and checking against those policies. The beauty of these aspects is that you're not planning them to be put in production. Essentially you're just using it to improve your personal productivity. And then you know how to really get the AOP way; you don't have to understand it, you just basically have to think AOP as a tool that helps you be a better developer. Once you're done with developmental aspects, how do you go about and use production aspects? Often the jump is too high for most people. Essentially you learn some basic ideas, but then when you go to production aspect they have to have the design you're comfortable with, that you maintain on long term. My suggestions is this regarding another class of aspects (Refactoring Aspects).

   

3. What are refactoring aspects?

Those aspects are like any other aspects, but there are 2 differences. The first one is about how you create those aspects and the second is what kind of aspects, what is the impact of those aspects.

How do you create those aspects? Normally you would just say "I don't have that functionality, let me just write an aspect and add that functionality, login, security, transaction."

With refactoring aspects you already have functionality. When we develop our system the cruft is there. What you do is you look at it and say "what can I do with all this code". You can take that code out and put it into an aspect. This was the first part, how to create aspects.

The second is the scope of these aspects, what is it going to impact? What you do with this refactoring aspect is you're usually targeting one class, maybe one package, so it's a rather limited scope. You're very deliberate about it. You're going to use some scoping constructs explicitly in aspects saing, "I don't care if I don't do any good. I want to make sure that I don't do any harm". Therefore these aspects tend to impact smaller portion, you can just unit test that portion and be confident that you haven't really affected functionality of anything else. Once you do this refactoring aspect you'll be more confident to deal with more complex functionality and then you can either graduate these aspects themselves to system-wide aspects or write new system wide aspects.

   

4. Can you give us some examples on what kind of code we can refactor into aspects?

Let's say you have method invocation from multiple places, let's say you're doing a check permission call in most methods ofa class. Those methods are representing one conceptual thing: "the access to this class is secure". That is one concept. But however new invocations are made in multiple places. You can take those method calls out and put it in an aspect and advise all those places to make the method call. That's one of the simple aspects.

The second kind of aspect, and that's where things start to become more interesting, is exception handling policy for a class.

Let's take an example: business delegate pattern. What is the typical implementation of pattern, you have a TRY block, you'll do your business delegation, you will catch any remote exception thrown during that processing during/that invocation, and then you will convert that exception into a business-specific exception and throw it. .So this whole try-catch blocks are repeated in many method, basically in all the methods that are doing delegation. Why not take all those try-cache block and put it in an aspect?

What we are really doing is saying "this class is implementing business delegate pattern", but then we have to say it everywhere. Now we are going to see that thing in one concept and mapping to one implementation inside an aspect.

Another simple aspect I see people using is providing default implementation for interface; we see lots of interfaces. Basically we end up writing boilerplate code; there isn't really much to it but we end up writing the boilerplate code in all the classes that are implementing this interface. In some cases we get our default class and we extend that default class, but since we don't have multiple inheritance, we can't always do this thing. We don't even want to do this thing for a variety of semantical reasons. If that is the thing, you can introduce default implementation into those aspects, into those interfaces and that's it; you don't have to write that code again and again.

There are other aspets like resource management aspects...basically if you see that you have one logical concept implemented in a certain class, or implemented across a few number of classes and that logical concept has a scattered implementation across multiple methods, across multiple classes, Then you can take that implementation and put it in an aspect. Once you understand the basic principles and basic techniques you can pretty much use it in many places without even thinking too much about it.

   

5. Most people think of aspects for modularizing cross-cutting concerns across objects. But you're talking about cross methods. Isn't that going a bit too far?

That's a good question. I call it micro-cross-cutting concerns, where you're dealing with refactoring aspects. It's an oxymoron. On one hand I call it micro, and on the other hand I call it cross-cutting.

Cross-cutting concerns are about having scattered implementation of some same concept across multiple modules. What does module mean? The whole application could be treated as a module or one class could be treated as a module. I'm going even lower-level by saying "what about multiple methods in a class?" They are also representing a concept they're they also have a modeule, they're doing some functionality that I can think about independently. That's basically what we look at as a module. If I can think about something independently in a smaller context, then that's a module.

Refactoring aspects, in a sense, are looking at this software concept of modules, smaller sized modules, and saying "if there's a cross-cutting concern, if there's that code-scattering and code-tangling problem, why don't apply AOP there? When you're creating an aspect to modularize a cross-cutting concern that's within an object, where's the best place to put that aspect? 600 The principle I apply is close to the source, close to the target. Suppose you're refactoring a class then put this aspect as close to the class as possible. What is the closest place you can get? It is inside of the class itself. That would be my first choice essentially. I'm refactoring a class and I'm going to put a private nested aspect.

Why this near the target principle? Let's say you're writing a refactoring aspect and you end up writing a pointcut that is slightly too heavily to depend on your class. You're saying "method a,b,c: I want to handle the exception in this particular fashion". Clearly your method "a" could be called method "z" in some evolution. A good example could be "debit method could be called withdraw method at some time". If your aspect is right in there then you can look at the point-cut and say "if I change the method name I'm going to change the point-cut expression as well to match this new method name". Therefore you kind of preserve the locality. Change is still there, within the same source file.

What it also means essentially is that you don't have to think too much about reducing dependency between aspects and classes. That's a big difference between refactoring aspects and system-wide aspects. That's another reason I say you should work with refactoring aspect for a while because there lets say suppose you do a pointcut is just enumerating a method, it's not ideal but it's not too bad. You can learn in the process like if you have to change a method how could you ave made a better pointcut. With system-wide aspects you don't want basically if any class changes you have to change this one aspect that is cutting across the system.

   

6. When one should put an aspect within an object versus externalized?

The guiding principle is this: is this the kind of high level concept that you're trying to model using an aspect? Let's say transaction management or security. These things usually tend to be going at the system and saying:"this is my security policy for the whole system".

It's basically a separate notion in a sense; it's a separate dimension; that puts those aspects the separate package you want. That would be the better way for packaging things. Some aspects are just class specific exception handling; those aspects can stay inside the class.

There's a third mixture of this. where you try to put abstract aspects or aspects that are not implementing the best policy for a concern into separate package and insert each class. You just write a simple aspect, and these aspects are usually specifying one or two pointcuts. There some mix-up design where you do get that freedom of writing point-cut that is a little bit closely matched to your class. Nevertheless you're not repeating the whole logic of what the security means.

   

7. You mentioned in the past that concurrency and resource management are also good applications for aspects. Can you explain ?

Resource management often involves that the method is acquiring some source, using and eventually releasing the resource. You will see that all the methods required that resource, are going to repeat that pattern. Acquire, use, release. Again we are dealing with one notion here: resource management. However the implementation probably does not reflect that reality. What we can do is we can write an aspect and put this acquiring and releasing portion into this aspect. We still keep the using portion inside the method itself because that is specific to method, it's not a cross-cutting concern. Let me give you an example: wait cursor- that is not a resource thing but it has the same characteristic like a resource. So you have your application and whenever you start an expensive method you want to set your cursor to wait cursor, do your stuff and at the end of the method you release the resource as in you set your wait cursor back to the one that you had before.

What happens is this: I've seen this simple pattern that everybody understands but almost nobody does it right. The biggest error I see is people should be putting try and finally, and the finally should be releasing the resource as resetting the wait cursor. But sometimes you miss it so what you see is an application, but wait cursor is there; you can still use the application but it looks annoying.

If you use an aspect you can take this policy and put it into an aspect and you don't have to ever worry about releasing the resource at the right moment or putting those try-finally blocks. That becomes like a questionable thing. It just like 5 or 6 lines of code to ensure that the wait cursor is released correctly. It's kind of a simple aspect that helps you a lot and bring consistency across your system.

   

8. What about concurrency? How can aspects be used to refactor concurrency control?

For concurrency control we see this kind of micro cross-cutting concern very strongly. Most methods have to participate in what policy you decide. Let's take a simple example: Synchronized, using synchronized blocks it's not a method-level decision if a method should be synchronized or not; essentially you have to have a class level policy that whoever is accessing any state of the class has to proceed in a synchronized block.

So what do you do? You put those blocks there and you end up seeing many methods repeating that characteristic; new methods get added to the system; hopefully the developer is careful and understands the policy and puts the right block or does not put it.

During evolution your policy may become weak. If you use aspect you're expressing this: these methods that I assign to be read only operations are going to do this policy, write-only operations are going to do this other policy. Putting in a context, consider read/write lock... management. Let's say you have an inventory management class.

Inventory management is a pretty good example where read/write lock helps because most of the time you'll be doing read operations, queryinh the inventory. Only once in a while you'll modify it.

You don't want to use synchronized blocks because then you're serializing access to this object. What you want is to take read-lock and read-method and release it once you're done. Take write lock in state modifying methods and release it at the end.

With this pattern you end up writing a lot of lines of code and then there are a lot of chances of making mistakes. Finally we talked about that during the resource management. The same thing happens; if you don't' have it and you take that lock and don't release it; your object may be pretty useless after that.

Or if you take read lock and by any chance releasing a write lock, again that can create quality problems. Hopefully capturing and understanding these things during testing but if not, that's a problem.

Put it in an aspect and then life becomes very simple. In one place you're saying "this is how I'm dealing with read operations, this is how I'm dealing with write operations and everything is taken care of".

   

9. Isn't it dangerous to lose the explicitness of putting in the code? If you get used to not coding in these constructs and you're used to relying on aspects, what if you forget to assign your point-cut?

That's a commonly asked question, essentially explicitness versus information hiding. The way I look at it is not all kinds of things should be aspectualized, that's an important point! Just because you see some core pattern repeated it does not mean you can apply refactoring and just put it there. Even if you can, it doesn't mean it's a good idea.

You have to look if these repeated code-patterns: are they representing the same semantic? Are they representing the same concept? If they are and then you take it out then you don't want to be dealing with them in every method. Because your method dealing with two ideas. Let's say concurrency control and business logic, or security and business logic, transaction management and business logic; or maybe all four of them.

If you don't take these implementations out and put them in aspects, you're getting modular at that level as well; essentially your business method is doing just the business logic. In a sense that's just what we want right? Even in object oriented programming, that was the idea: simple responsibilities, simple roles and hopefully only one responsibility and one role. Aspects let you do that even when there are cross-cutting concerns that your method would know how to deal with.

   

10. You mentioned that you could find yourself doing repetition even within aspects. Can you give us an example? How would you solve that problem?

What happens is this: when you're trying to do refactoring you end up writing these aspects. Let's say you have a class inventory management that has concurrency control policy (read-write lock based) so you refactor it out and these aspects would be like 10-15 lines of code. Then you look at another class and that also has the same policy. Chances are that you may even copy paste that code and then just refactor the code out and put it in an aspect. You will see that even aspects may start to have the characteristics of do not repeat yourself, essentially violation of that. What do you do at that point? You look at the aspects - these become your target units and say "what is happening here?". The advice portion is being repeated. You're doing the same thing in all the places. Then what we do is we apply these extract-base aspect which is basically an extension of extract base-class refactoring. You essentially take out that the advice and put it in the base aspect; you make those point-cuts abstract in those base-aspects, concrete aspects are now going to have only implementation of abstract point-cuts or if you need abstract methods. Your aspects are now only saying one thing: essentially what do I want to select from my host class and that simplifies your design.

   

11. Any final words on AOP refactoring?

If you exhaust doing logging and testing and say "ok what do I do now?, and now you want to apply aspects in a more interesting fashion, look at refactoring aspects. These aspects also have a very good educational value. Try those out. In just a few months you'll become very comfortable with AOP, the AOP way of thinking; you'll start to see these aspects even a lot more than what I'm describing and what I'm describing in my book. And you start to get into this AOP way of thinking. Once you're done with it, go to applying AOP in a more system-wide fashion, and you'll be glad you did it.

May 07, 2007

BT