The Source for Java Technology Collaboration
User: Password:



Bob Lee

Bob Lee's Blog

The AOP Elevator Speech

Posted by crazybob on September 20, 2004 at 08:37 AM | Comments (11)

So what's this AOP thing I've been hearing about?

Aspect-Oriented Programming (AOP) picks up where OOP leaves off. AOP enables me to abstract boilerplate code into one place as opposed to scattering it throughout my code base. AOP lets me say, "apply some code that follows this generalized pattern to all of these places."

Say for example that I have a form class that holds data from a screen. The class has multiple setter methods for the fields and an isValid() method that returns true if the form fields are valid.

  class LoginForm {
    ...
    void setUserId(String userId) { ... } 
    void setPassword(String password) { ... }
    boolean isValid() { ... } 
  }

My code calls the isValid() method multiple times, and it takes a non-trivial amount of time to execute (perhaps it validates against a remote system). It makes sense to cache the result and blow it away any time the form's state changes.

With traditional OO, code in each setter could delete the cached value, and the template design pattern could abstract the caching logic from isValid() into a method in a super class. Unfortunately, adding caching logic impacts all of my form classes. The more form classes, the more changes. I have to put code in every setter (running the risk of accidentally missing one), and now I have to modify the form classes to implement a template method instead of isValid(). Lastly, reusing code through inheritance couples validation caching with other such reusable functionalities in the parent classes preventing me from reusing them independently.

AOP enables me to leave my form classes untouched and to fully decouple reusable functionalities. My form classes shouldn't care that I'm caching the validation result. With AOP, I can implement the caching logic in one module and apply it at runtime or build time to all of my forms or to other places that follow a similar pattern. Each new form I add will enjoy caching for free. I've gone from a constant maintenance effort to zero effort thanks to AOP. Plus, I have a lot less code to unit test.

Piqued your interest? Take Dynaop for a spin.


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first)

  • AOP Command Pattern
    I've been wondering about applying AOP to JSF and it's use of action methods. If I want to call #{order.save}, I could force dependencies on having other session beans validated.

    With straight OOP:

    public String save() {
    String view = getUser().validateOrdering();
    if (view != SOME_VAL)
    {
    // actually save order try/catch
    view = SOME_OTHER_VAL;
    }
    return view;
    }


    Look at Struts. It provides many features in its framework that mimic AOP-like behavior including chaining and exception handling.

    Why not just use simple beans and enhance the controller behavior with AOP?

    Posted by: jhook on September 20, 2004 at 03:20 PM

  • AOP Command Pattern
    AOP would be great for any framework which provides callbacks, hooks, and so on. AspectJ's before/after advice are the general form of these concepts. Instead of implementing an interface with methods like beforeInit() and afterInit(), you could just define the additional behaviour as advice which are executed at the respective joinpoints. This would make the frameworks slim and easier to evolve.

    Unfortunately, AOP came up about five years too late to become a core technology of Java; therefore, it seems that framework developers don't consider it an option for their products.

    Posted by: el_barto on September 21, 2004 at 02:04 AM

  • Recursive weaving
    Bob,

    Probably, this is not the most suitable of your posts for my reply, but anyway…

    With any AOP framework I've been used (DynAOP, Nanning, JBoss-AOP), I faced the same interesting problem: after weaving of certain aspects to original object, result becomes a target for further "aspectization" on its own. And neither framework handles this automatically, at least I‘ve seen no framework so far that applies weaving recursively.

    Let us consider the following example. We have aspects’ configuration that says to apply transaction interceptor to all methods of interface ISample. On other hand, we have pointcut that tells to introduce ISample on implementations of interface IAnything. The result we get after first weaving cycle, let us say ProxyFactory.wrap(target) for clarity, is quite interesting: targets these were ISample before weaving get transaction interceptor; targets these were IAnything instances get ISample introduction but WITHOUT transaction interceptor.

    The result above is quite predictable: pointcuts are checked against current target type, but not resulted type. What is necessary is to re-apply aspects on result i.e. make one additional call to ProxyFactory.wrap(justWrapped).

    What is interesting here is a question whether or not this process is finite and could be implemented automatically by framework. Obviously, when we apply aspects on class-level (Introduction, Mixin) we create potential targets for method-level aspects (Interceptors). If we have more “smart” pointcuts these operate on target instance properties or invocation arguments we are potentially at risk of infinite recursion (or not?).

    Any thoughts on this issue?

    Thanks,
    Valery

    P.S. I don't remember exactly, but either Rickard or Cedric point to this issue some time ago.

    Posted by: silaev on September 21, 2004 at 05:10 AM

  • Recursive weaving
    Unfortunately I haven't used the frameworks you mentioned, but you should be able to avoid this problem by appropriately defining your pointcuts. To see how this is done in AspectJ see sample chapter 3 from "AspectJ in Action" (www.manning.com).

    Posted by: adamjaph on September 21, 2004 at 06:08 AM

  • Nice expanation but...
    ...do you think you could say it all in a single elevator trip?

    Ian.

    Posted by: ifairman on September 21, 2004 at 08:46 AM

  • Recursive weaving
    Dynaop addresses this. You can't use a ClassPointcut here. ClassPointcuts only pick out target classes (not the proxy class which you've introduced the interface in). You need to use a MethodPointcut. They pick out all of the methods in the proxy class, including the introduced ones. The MethodPointcut for your example looks like this:

    declaringClass(ISample)

    ClassPointcuts are best used as high level filtering mechanisms (to limit your advice to a given package or class) and as arguments to MethodPointcuts.

    Posted by: crazybob on September 21, 2004 at 09:04 AM

  • Recursive weaving
    Just to clarify, this will apply a TransactionInterceptor to all methods in ISample including those introduced by other advice. You may declare the ISample introduction before or after this interceptor declaration in the dynaop.bsh file. Ordering doesn't matter; dynaop introduces mixins first and then applies interceptors.

    interceptor(ALL_CLASSES, declaringClass(ISample), TransactionInterceptor);

    Posted by: crazybob on September 21, 2004 at 09:08 AM

  • Nice expanation but...
    Works for our elevators. You may have to summarize or talk very quickly if you work in a shorter building. ;)

    Posted by: crazybob on September 21, 2004 at 09:11 AM

  • Good example
    I'm normaly contrarian about AOP, but this is a very good example. Like you say, there's no easy way to get update notifications across a classification of objects with regular OOP. I'd characterize the problem more along the lines of knowing if an object is clean or dirty, ie, has it been modified since.

    The only concern would be that the non-trivial isValid() call may rely on factors that aren't static. If it really does call out to some service, the rules governing isValid() may have changed.

    My overall nagging concern is that the aspects aren't explicit in the code. Is this concern valid, or overblown? I guess IDE's will be able to tell us which aspects are being applied.

    Taylor

    Posted by: tcowan on September 22, 2004 at 08:08 AM

  • Good example
    I started out thinking in terms of clean/dirty, but when I actually implemented it, the easiest thing to do was reuse my observer pattern aspect that I already had and write a caching interceptor which observed the object. When the interceptor gets notified of state changes, it blows away the cached value. The whole thing ended up being a few lines.

    Good point about external factors invalidating the cached value. In our case, isValid() does a lot of work and we call it often.

    The concern about the aspect not being explicit in the code is perfectly valid. It's a tradeoff, less code, more complexity.

    Posted by: crazybob on September 22, 2004 at 08:20 AM

  • Why not
    interface Validator {
    boolean IsValid(u,p)
    }
    class CachedVailidator implements Validator {
    boolean isValid(u,p) {..}
    }
    class Form {
    Validator v = new CachedValidator();
    ...
    }
    if (v.isValid(uname,passwd)) ...

    Posted by: fsickert on October 09, 2006 at 07:51 PM





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds