Aspect-oriented programming has the potential to help every developer write higher-quality code in less time. Here's how to declaratively apply custom methods implementing common functionality to your code.
In a recent discussion with the applications development manager of a major global company that uses the Microsoft .NET Framework as its development platform, I was asked how his department could increase productivity. We discussed several approaches, including increased code reuse. That discussion led me to take a deeper look at aspect-oriented programming (AOP). What I found is that AOP can increase code reuse within targeted functional areas, while also improving code maintainability and readability. AOP has the potential to help every developer write higher-quality code in less time.
What Is AOP?
At its core, AOP is the practice of encapsulating functionality common among multiple code elements yet independent of the business logic, and applying that common functionality to the target source code elements declaratively.
The common functionality is referred to as a crosscutting concern because it "cuts across" the entire application as a capability that's useful, independent of the business logic. Common examples include error handling, logging, tracing, instrumentation, transaction handling, security, caching, data binding, undo/redo and even multithreading. None of these 10 examples involves business logic, but they're all common and often necessary capabilities in any high-quality application.
Benefits of AOP
The code that implements one of these crosscutting concerns is known as an "aspect," and thus gives rise to the term "aspect-oriented programming." What benefit does it provide the developer if the functionality for these aspects can be removed from the primary source code and declaratively applied as needed to the application?
I'm truly excited by the benefits of AOP:
The business logic source code is much easier to read and understand, without the clutter of the code needed to support crosscutting concerns.
Less repetition in the code of the business logic means shorter developer times, fewer defects and a less-tedious coding experience.
Developers can better focus on implementing the proper business logic.
Less-experienced developers won't have to learn to weave these crosscutting concerns throughout their code, and experienced developers will appreciate more fluid coding by focusing primarily on business logic.
To be clear, the code for these crosscutting concerns is still present in the application; it has simply been moved into aspects. Writing these aspects generally requires a more experienced developer, so they tend to be high-quality code. These aspects are added to the application by the AOP framework at specific execution points known as "join points," such as at the time of object instantiation, method initiation, method exit, property access and so on. A set of join points is known as a pointcut.
History of AOP
AOP isn't something recently created to help .NET developers. Work on AOP started in the Xerox Palo Alto Research Center (PARC) in 1996, which is the same group credited with giving us laser printing; the Ethernet network protocol; the modern PC GUI using windows, icons and a mouse; object-oriented programming (OOP) via Smalltalk; and very-large-scale integration (VLSI) for semiconductors. PARC research into AOP resulted in the design of what is now AspectJ for the Java language.
Since 1996, AOP extensions have been developed for multiple languages, including C++, Ruby, PHP, SAP ABAP and even COBOL. Visual Studio 2012 and earlier versions do not directly support AOP. The aspect-like capability to declaratively apply custom code to methods can be found in Windows Communication Foundation (WCF) declarative filters and ASP.NET MVC dynamic filters.
The Path to AOP
In order to fully implement AOP in a .NET application, you want the ability to move crosscutting concerns into separate aspects, apply them declaratively to one or more source code locations and -- without changing the application architecture -- enable the use of aspects. While the source code modified by the aspects may be aware of the aspects by leaving certain tasks to them, the source code should be ignorant of the internal implementation of the aspects.
One way to add aspects into an application is to wrap each class with a dynamic proxy (outer shell) to provide additional functionality around the class, but the benefits of this approach are limited. Most dependency-injection frameworks in .NET provide some support for proxy-based AOP, which can be useful for logging, instrumentation and caching. This approach is only able to inject behaviors into the communication between a service and its consumer -- not inside the service itself -- so it can have the disadvantage of altering the application architecture to support aspects.
Another way to add aspects to an application is through code generation, but this, too, is limited. This approach allows for more powerful code transformation, but requires recompilation of the application each time any of the aspects change.
A third way to add aspects is through metadata-driven programming, utilizing dynamic languages such as IronPython or IronRuby. This can allow additional functionality to be added at runtime via aspects, but the complexity of implementing your own AOP framework is prohibitive.
To see how to implement AOP in .NET using a full-featured AOP framework, I looked at PostSharp, which claims to be the most comprehensive aspect-oriented software available for .NET.