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.
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.