Saturday, September 27, 2008

AOP: what is it and why do I care

Aspect Oriented Programming or AOP. This is Yet Another Acronym (or YAA ) but for most people and most cases it's really just another recipe to solve problems in your programmer cookbook. There are many resources out there on AOP on the web and I won't attempt to cover all details of AOP.

For those new to the concept, AOP will be most clear to you through the lens of logging concerns. How many of you have written or seen code like the following?:

DoSomething action= new DoSomething();
string result = action.Execute();
Logger.Log("Info", result);
Or even worse:

DoSomething action = new DoSomething();
Customer result = action.Execute();
LogCustomer.Log(result.FullName, result.Address, result.Phone);


Only you see it every 3 lines and scattered everywhere, throughout the code base. Do something as simple as changing logging framework, changing what you log or adjusting your static logging class can end up being a nightmare. AOP attempts to solve this more cleverly by using a combination of rules called "pointcuts" to match what you log, and then executing functions called "advices" .

The flexibility and implementation of pointcuts vary greatly from one AOP framework to another, so you'll have to consult your framework of choice's docs. But I can give you an example using IoC container Castle Windsor:
1: WindsorContainer container = new WindsorContainter();
2: container.Register(AllTypes.Of.FromAssembly
(Assembly.GetExecutingAssembly()).Interceptors.Add(new InterceptorReference(typeof(LoggingAdvice))).Last));

Now lets recap what's going on here? First line initializes the container, second line registers all classes that implement the ICommand interface in the currently running assembly, and then adds the LoggingAdvice. You can change this to all classes, or only individual classes but that's an exercise I leave up to the reader. Lets show the code now:

public interface ICommand{
string Execute(string sendString);

}

public class Command: ICommand
public string Execute(string sendString)
{
string toconsole = sendString + " this is from the console";
Console.WriteLine( toconsole);
string retvalue = sendString + " this is returned by the method";
return retvale;
}

This is a simple enough class. It writes the sent string to the console with some additional information, then returns the sent string with where it is in the Execute Method.

And the LoggingAdvice is as follows:

public class LoggingAdvice:IInterceptor
{

public void Intercept(IInvocation invocation)

{
invocation.Proceed();
Object argument = invocation.GetArgumentValue();
Console.WriteLine(argument.ToString() + " from LoggingAdvice" );
Object returnValue = invocation.ReturnValue;
Console.WriteLine(returnvalue.ToString() + " from LoggingAdvice" );

}


}
note the invocation.Proceed() method call. This ends up having the "Advised" or "Intercepted" method execute then and there. Then the invocation will be able to get the return value. This means you could move the proceed call to just before the ReturnValue call.

Now client code would look something like so:

class Program{

static void Main(string[] args)

{
WindsorContainer container = new WindsorContainer();
//add registration code from above here
ICommand cmd = container.Resolve();
cmd.Execute(args[0]);
}
What's going on here? Windsor retrieves an instance of ICommand which will give you the Command class. The Execute is called on the ICommand instance with the args from the command line passed in. You'll find a result similiar to so when run from the command prompt:

1: program.exe "doing stuff"
2: doing stuff this is from console
3: doing stuff from Logging Advice
4: doing stuff this is returned from the method from Logging Advice


Let's recap, our core code is making no direct calls to a logging class and it is not sprinkled all over our code. While in this trivial example the gains are not realized but anything larger than 300 lines of code we're going to see a benefit from using an AOP framework.

In upcoming posts I'll cover other frameworks and more common uses of AOP.

No comments: