As one of the core contributors to a compiler for a new language, I often find myself needing to explain new concepts and ideas to other developers. Over time, I've gradually found an approach that tends to work. First, where does the concept fit on the language feature or paradigm map? Second, what is the problem that it is aimed at, and how are other perhaps more familiar solutions weak? And then -- with that critical foundation laid -- what does the new concept actually do to help?

Recently, I was asked to speak about Aspect Oriented Programming at .Net Community Day in Sweden. I've known about AOP for years and discussed it with developers before, but this was a nice opportunity for me to spend a while thinking about how best to go about explaining it. So I set about coming up with the AOP answers for my three questions.

The paradigm jigsaw

There are a bunch of titles that we tend to put before the word "programming". "Object Oriented Programming", "Functional Programming", "Declarative Programming" and "Generic Programming" are just some examples. They're all paradigms, but the amount they try to answer differs. The first three I listed -- OOP, FP and DP -- will happily jostle for pride of place in a language, seeking to be its "core paradigm", and posing a challenge to language designers who see value in all of them. Of course, some languages do decide to just follow a single paradigm: regexes are happily declarative, and Java has admitted little into the language that isn't squarely inside the OO box. However, most of the popular and expressive general purpose languages out there today embrace multiple of these "core paradigms", recognizing that solving every programming task with a single paradigm is like doing every garden task with a lawnmower.

At the same time, there are paradigms that simply seek to deal with a very particular set of issues. Generic programming is one of them: it deals with the situations where we want to use the same piece of code with different types, in a strongly typed way. This idea actually cross-cuts the "core paradigms"; generics are simply a case of parametric polymorphism, which one finds in functional languages.

The purpose of Aspect-Oriented Programming

This is the kind of place that AOP sits. It's not pitching itself as a successor to -- or even a competitor of -- any of our familiar core paradigms. Instead, it takes a problem and proposes an approach for solving it. So what's the problem?

In any complex code base, there are cross-cutting concerns -- things that we need to do in many places that are incidental to the core logic. For example, logging and exception handling and reporting are generally things that a piece of code does in addition to the task that it really exists to perform. Of course, we factor as much of the logging and reporting code out as we can, but generally we find ourselves doomed to repeat similar-looking bits of cross-cutting logic in many places. Even if it's just a call to the logger, or a catch block, it ends up duplicated.

Duplicated code is generally a bad sign. For one, it inhibits future refactoring; if we decide to switch logging library, this is now a change in many places. Since we have mentioned the logging library in many places, we have many pieces of our code that are closely coupled to it -- something that goes against our general preference for code that is loosely coupled. We also run into challenges when we want to install new cross-cutting concerns into our software -- there's so many places to change!

Aspect Oriented Programming is aimed at these kinds of challenges. It introduces the concept of join points -- places in our code that we could add some functionality -- and advice -- the functionality that we want to incorporate. It provides a mechanism to write the cross-cutting functionality in one place and have it "inserted" into the join points.

Managing complexity

A common reaction is, "wow, magical", but if we cast our eyes back over programming history, it's really not so surprising. Once upon a time there was just assemblers. Repeating regular sequences of instructions was tiring, so macro systems were created. These "magically" got substituted for a sequence of instructions, with some parametrization thrown in to make them more powerful. A little further on, named and callable subroutines allowed for real re-use of code. We raised the abstraction bar: "you don't have to worry about what this bit of code does, just call it like this". Then came OO. Here things got really magical: invoking one of these method things leaves you with no idea what bit of code is going to be called! It could come from that familiar bit of code you wrote yesterday, or it could come from a subclass that's going to be written in 10 years time.

Over the years, we've evolved ways to manage complexity in software, many of them hanging off increasingly non-linear or loosely coupled flow of control. We've coped with the increasing complexity in our paradigms and programming style, because they more than pay themselves back by decreasing complexity in the software we produce using them. AOP's concept of join points and advice is giving us a little more conceptual overhead in exchange for a more maintainable and less coupled implementation of our cross-cutting concerns.

Looking to the future, I think it's important to remember that where we are with any paradigm today is almost certainly some way from the ideal. We've learned a lot about object oriented programming over the decades that it has existed, and continue to do so. Multiple inheritance has generally been marked down as a bad idea, and the emergence of traits in Smalltalk (also known as roles in Perl 6) has made some -- myself included -- question whether we'd be better off dropping inheritance altogether in favor of flattening composition.

AOP has only had 15 years of lessons learned so far, and I've no doubt we've got a long road to walk from here. However, that being the case shouldn't stop us from embracing AOP today. After all, the ongoing journey of the object oriented paradigm isn't something that stops us from using it. The critical question is, "does this help me deliver higher quality, more maintainable software" -- and I think there are a bunch of use cases where today's implementations of AOP help us to do so.

About The Author Jonathan Worthington is an architect and programming mentor at Edument. He works to help development teams improve their software architecture and work more efficiently, following best practices. He is also one of the core developers of the Rakudo Perl 6 compiler, where his work focuses on the implementation of object oriented language features and the type system. In his free time, he enjoys hiking in the mountains, good beer and curry.

According to Microsoft, “AppFabric Caching provides a distributed, in-memory, application cache service for Windows Azure and SQL Azure applications - giving high-speed access, scale, and high availability to application data.”

What's more, these capabilities are provided entirely as a service (no installation or management of instances, dynamically increase/decrease cache size as needed).

In our very first Live Webinar, Gael Fraiteur (SharpCrafters Founder) and Kendall Miller (Gibraltar Software Co-Founder) speak with special guest Brian Prince (Microsoft) about AppFabric Caching capabilities and show you how to use PostSharp + Gibraltar to AppFabric enable your applications.

This webinar includes:

AppFabric concepts walkthrough

DIY caching versus caching with PostSharp

Advanced caching problems

Monitoring cache effectiveness and performance with Gibraltar

AppFabric futures

PostSharp futures

Q&A

This live webinar is free and kicks off Thursday, February 24th at 10:00am PT

Sometimes there's just no way to speed up an operation. Maybe it's dependent on a service that's on some external web server, or maybe it's a very processor intensive operation, or maybe it's fast by itself, but a bunch of concurrent requests would suck up all your resources. There are lots of reasons to use caching. PostSharp itself doesn't provide a caching framework for you (again, PostSharp isn't reinventing the wheel, it's just making it easier to use), but it does provide you with a way to (surprise) reduce boilerplate code, stop repeating yourself, and separate concerns into their own classes.

Suppose I run a car dealership, and I need to see how much my cars are worth. I'll use an application that looks up the blue book value given a make & model, and year. However, the blue book value (for the purposes of this demonstration) changes often, so I decide to use a web service to look up the blue book value. But, the web service is slow, and I have a lot of cars on the lot and being traded in all the time. Since I can't make the web service any faster, let's cache the data that it returns, so I can reduce the number of web service calls.

Since a main feature of PostSharp is intercepting a method before it is actually invoked, it should be clear that we'd start by writing a MethodInterceptionAspect:

I store the method name at compile time, and I initialize the cache service at runtime. As a cache key, I'm just appending the method name with all the arguments (see: BuildCacheKey method), so it will be unique for each method and each set of parameters. OnInvoke I check to see if there's already a value in the cache, and use that value if there is. Otherwise, I just Invoke the method as normal (you could also use "Proceed" if you prefer) and cache the value for the next time.

In my sample CarDealership app, I have a service method called GetCarValue that is meant to simulate a call to a web service to get car value information. It has a random component so that it returns a different value every time it's called (only where there is a cache miss in this example).

I could have also used OnMethodBoundaryAspect instead of MethodInterceptionAspect--either approach is fine. In this case, using MethodInterceptionAspect was the simpler choice to meet the requirements, so that's why I chose it.

Note that the ICache dependency is being loaded in the static constructor. There's no sense loading this dependency when PostSharp is running, so that's why that check is in place. You could also load that dependency in RuntimeInitialize.

This aspect doesn't take into account possible 'out' or 'ref' parameters that might need cached. It's possible to do, of course, but generally I believe that 'out' and 'ref' parameters shouldn't be used much at all (if ever), and if you believe the same then it's a waste of time to write out/ref caching into your aspect. If you really want to do it, leave a comment below, but chances are that I'll put the screws to you about using out/ref in the first place :)

Build-Time Validation

There are times when caching is not a good idea. For instance, if a method returns a Stream, IEnumerable, IQueryable, etc, these values should usually not be cached. The way to enforce this is to override the CompileTimeValidate method like so:

So if any developer tries to put caching on a method that definitely shouldn't be cached, it will result in a build error. And note that by using IsAssignableFrom, you also cover cases like FileStream, IEnumerable<T>, etc.

Multithreading

Okay, so we've got super-easy caching that we can add to any method that needs it. However, did you spot a potential problem with this cache aspect? In a multi-threaded app (such as a web site), a cache is great because after the "first" user pays the cache "toll", every subsequent user will reap the cache's speed benefits. But what if two users request the same information concurrently? In the cache aspect above, it might be possible that both users pay the cache toll, and neither reap the benefits. For a car dealership app, this isn't very likely, but suppose you have a web application with potentially hundreds or thousands (or more!) users who request the same data at the same time. If they all put their requests in between the time it takes to check for a cache hit and the time it takes to actually fetch the value, hundreds of users could incur the "toll" needlessly.

A simple solution to this concurrency problem would just be to "lock" the cache every time it's used. However, locking can be an expensive, slow process. It would be better if we check if there's a cache miss first, and then lock. But in between the checking and locking, there's still an opportunity for another thread to sneak in there and put a value in cache. So, once inside the lock, you should double-check to make sure the cache is still not returning anything. This optimization pattern is called "double-checked locking". Here's the updated aspect using double-checked locking:

It's a little repetitive, but in this case, the repetition is a good trade-off for performance in a highly-concurrent environment. Instead of locking the cache, I'm locking a private object that's specific to the method that the aspect is being applied to. All of this keeps the locking to a minimum, while maximizing the usage of the cache. Note that there's nothing magical about the name "syncRoot"; it's simply a common convention, and you can use whatever name you'd like.

So, confused yet? Concurrency problems and race conditions are confusing, but in many apps they are a reality. Armed with this PostSharp aspect, you don't have to worry about junior developers or new team members or the guy with 30 years COBOL experience using C# for the first time introducing hard-to-debug race conditions when they use the cache. In fact, all they really need to know is how to decorate methods with the "Cache" aspect when caching is appropriate. They don't need to know what cache technology is being used. They don't need to worry about making the method thread-safe. They can focus on making a method do one thing, making it do one thing only, and making it do one thing well.