What is Aspect Oriented Programming?
This article will give you a brief introduction to Aspect Oriented Programming using an upcoming library product that’s being developed at RemObjects. The project is currently codenamed “RemObjects Taco”. Taco is a library that will enable you to leverage concepts of Aspect Oriented Programming (AOP) in your .NET applications. Unlike other aspect oriented tools available, Taco is a language-independent library and will allow you to both use and implement aspects using the .NET language of your choice.
Aspect Oriented Programming is based on the concept of expanding and specializing class implementations, not by extending their code or using traditional inheritance models, but by attaching pieces of code, called Aspects, to them.
Assume that you have a fairly extensive class with many methods, and you now are faced with the task of applying a certain layer of logic to that class. This layer could involve thread synchronization, security checks, or something as simple as logging all method calls to a log file for debugging or audit purposes.
If the class in question is fairly extensive and contains a large number of methods, adding code for that purpose to each and every method would be a huge amount of work. It would also involve adding a lot of duplicate code to your library, and adding it in places where it does not really belong. (After all, a method should focus on the task at hand and should not be weighted down with external “plumbing.”)
With Aspect Oriented Programming, you implement your logic in a separate Aspect class, independently of the class (or classes) you will later want to augment. Once the aspect is implemented, you can easily attach it to any given class in your class library, and your logic will be applied to all (or selected) calls made into the class. Neither the class nor the caller will need to worry or even be aware of the aspect. For instance, you can use this technology to add a CriticalSection; code for checking the user’s access rights; or code for writing data to a log file. All of this will be implemented in a separate class called an Aspect and will not clutter up your primary code.
An Example
Let’s look at an example to illustrate this concept by implementing an aspect that performs thread synchronization. Taco already comes with a prebuilt Synchronize aspect to provide this functionality and a lot more flexibility then the example shown; but for the purpose of this article let’s assume that thread synchronization represents some custom logic you want to implement yourself.
Let’s assume that you have a (completely contrived) MyData class already implemented. While scaling up your application to be multi-threaded, you find that it would be helpful if the MyData class was thread-safe (which the current implementation isn’t). Here is your existing code for the MyData class:
type
MyData = class(MyBaseClass)
private
fValue: integer;
public
method Calculate;
property Value: integer read fValue write fValue;
end;
end;
implementation
method MyData.Calculate;
begin
fValue := (fValue+3)*5;
end;
To make even this simplistic class thread-safe using conventional programming techniques would involve a an amount of code that far exceeds the current class implementation - you’d have to
- add a private field to hold a CriticalSection or Mutex
- add a constructor to initialize the critical section
- add calls to CriticalSection.Enter/Exit with corresponding try/finally blocks to all methods
- add getter/setter methods for the property so that you could acquire the critical section for property access.
In contrast, using Aspect Oriented Programming and assuming that you have implemented your Synchronize aspect, you would add exactly one line of code to your class definition and it will be thread-safe automatically:
type
[Synchronize]
MyData = class(MyBaseClass)
private
fValue: integer;
public
method Calculate;
property Value: integer read fValue write fValue;
end;
end;
implementation
method MyData.Calculate;
begin
fValue := (fValue+3)*5;
end;
With the exception of the [Synchronize] attribute added to the class declaration, the code is completely identical to the original version. The individual methods and properties are unchanged and not cluttered with synchronization code.
Implementing the Aspect
Now that you’ve seen how to augment a class with an aspect, let’s take a look at what’s involved with writing a custom aspect (in this case, we’ll implemented a simple version of the Synchronize aspect used above).
Taco provides a base class (RemObjects.Taco.Aspect) for you to descend from to implement your own aspects. All we need to do is create a descendant, instantiate a Mutex object, and implement the PreprocessMessage and PostprocessMessage methods to acquire and release the Mutex, respectively:
type
SynchronizeAspect = assembly class(Aspect)
private
fLock: Mutex := new Mutex();
protected
method PreprocessMessage(aMessage: CallMessage); override;
method PostprocessMessage(aMessage: ReturnMessage); override;
end;
implementation
method PreprocessMessage(aMessage: CallMessage);
begin
fLock.WaitOne();
end;
method PostprocessMessage(aMessage: ReturnMessage);
begin
fLock.ReleaseMutex();
end;
The PreprocessMessage method of your aspect will be executed prior to any call into the classes augmented with your aspect and the PostprocessMessage method will be executed after any of the calls return. This happens whether they return successfully or were aborted via an exception. Note that the aMessage parameter also gives you access to details about the call being made (such as which object is being called, which method, and what parameters are being passed or returned.) While this simple aspect didn’t need this information, it is available for more complex aspects.
How Does this Work?
Underneath the hood, Taco uses .NET’s messaging architecture, which is also used by .NET Remoting, to enable the injection of code. Basically, every method or property call made to your augmented object will be run thru a number of Message Sinks before reaching the actual method. Taco aspects hook into this list to execute the logic you provide in the PreprocessMessage and PostprocessMessage methods.
Converting the method call to a message that can be processed by your aspect and back does of course introduce a small amount of overhead. This hit, however, is not serious. The overhead is comparable to calling an object from a different AppDomain (which basically uses the same technique) or a COM object hosted in the COM+ runtime. For normal “business logic” type object hierarchies, this overhead will be negligible, but you probably would not want to use AOP inside the core rendering engine of your new first person shooter game!
The above code snippets are written in Chrome, but the same principles apply to other .NET languages.
Please also note that Taco is currently in early alpha state, so the exact class interfaces and syntax shown in the last code snippet below might still be subject to change before public release. If you’re interested in joining the Taco beta program, when it becomes available, please drop me a mail.
Further Reading
Hopefully this article has given you a quick introduction to Aspect Oriented Programming and has given you a sense of the scope of what Taco will provide for .NET developers seeking to use AOP.
The links below provide some more general information on AOP:
- AOP: Aspect-Oriented Programming Enables Better Code Encapsulation and Reuse — MSDN Magazine, March 2002
- Aspect-Oriented Software Development Community
- LOOM.NET