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.