If you're an AOP framework you're generally implemented in one of two ways; either you're compile-time and the code is "weaved" into the class of choice or you're runtime and a proxy is generated for the class in question. Basically, you're looking to attach some behavior to a class that will happen before a method call, after a method call or when an exception is thrown.
AOP being a tool in my six-demon bag, I have to see how it can improve any of my current projects. Right now, I only have the ubiquitous logging issue that is commonly mentioned in the same breath as AOP. The usual problem is thus; you have some component who's main concern is not logging wonderfully verbose diagnostic information but rather that it builds a list of accounting records for export to a general ledger or it's approximating how late an MBTA train can be before the commuting populace riots. To keep the gods of Truth and Beauty satisfied you must separate these concerns.
My only problem is that I may have already. I hadn't seen it done this way before but I was using the decorator pattern to effectively perform the same work that an AOP framework would. I had fallen into using decorator pattern recently to eliminate inappropriate inheritance (just about any inheritance is inappropriate to me but that's a topic for another post). Think of it this way; the AOP framework is providing a mechanism to intercept method calls and do some magic before or after the a method is executed. The decorator pattern is doing just that. I'll break down the pros and cons for using an AOP framework or using the decorator pattern in a moment but here's a code snippet to demonstrate the thought.
NOTE: For the sake of brevity I'm hoping you can make logical leaps about the types and classes I'm using. They're purely fictional and they're not what's important.
public interface IComicBookRepostitory
{
ComicBookCollection GetComicsBy(SearchParameters searchParams);
}
An IComicBookRepository is simply a repo for comic books. In this case it has a method for retrieving comic books by a set of parameters. The concrete class for the repo looks like the following.
public class ComicBookRepository : IComicBookRepostitory
{
public ComicBookCollection GetComicsBy(SearchParameters searchParams)
{
return new ComicBookCollection();
}
}
This class obviously does nothing of import but that's ok. We can fill this in with whatever we please when we know what that implementation will look like.
I can create a class that will "decorate" any instance of IComicBookRepostitory with error logging. Hence the LoggedComicBookRepository.
public class LoggedComicBookRepository : IComicBookRepostitory
{
private ILog _log;
private IComicBookRepostitory _wrappedRepo;
public LoggedComicBookRepository(ILog log, IComicBookRepostitory wrappedRepo)
{
_log = log;
_wrappedRepo = wrappedRepo;
}
public ComicBookCollection GetComicsBy(SearchParameters searchParams)
{
try
{
_wrappedRepo.GetComicsBy(searchParams);
}
catch (ArgumentException ex)
{
_log.Error(
string.Format("Parameters: Genre {0}, Year {1}", searchParams.Genre, searchParams.Year), ex);
throw;
}
}
}
Notice that it contains an instance of an IComicBookRepostitory. The LoggedComicBookRepository has a constructor that is configured with an ILog and an IComicBookRepostitory. The ILog will capture logged messages and the IComicBookRepostitory is what is being wrapped (decorated per the pattern). Whenever the wrapped instance of the IComicBookRepostitory throws an error it will be captured and the appropriate error logging will occur. Calling GetComicsBy(searchParams) is now completely logged and our concrete ComicBookRepository is none the wiser. Likewise, if we wanted to do more work before or after the method is called for any other reason then we may do so and capture the essence of AOP.
Here's some setup code that will perform comic book retrieval that will be logged.
public class SomeClass
{
public void DoSomething()
{
IComicBookRepostitory repo =
new LoggedComicBookRepository(
log4net.LogManager.GetLogger(GetType(LoggedComicBookRepository)),
new ComicBookRepository());
SearchParameters searchParams = new SearchParameters(Genre.Action, 1995);
ComicBookCollection collection = repo.GetComicsBy(searchParams);
}
}
That setup/configuration code can easily be hidden behind a DI container or something similar but I'm just illustrating how it would generally work.
The advantage to this is that I don't need to bring in a new library with XML configuration, code setup and an API that may not be familiar to other developers. It's also swatting a fly with a bazooka. In the scenario presented above we really don't need to involve a professional AOP solution.
Where AOP makes sense is for a more generic scenario. With the code I've been working with, I want to emit important information about specific method calls and their inputs. If I didn't care for that then an AOP framework would be a far better choice. For example, if I wanted to include code that profiled method execution then I could have a stopwatch that started on method entry and stopped on method exit using an AOP framework. The only thing it may capture about the method is its name. If I tried to hack an AOP framework into my previous example I would have to generate additional code that would provide mapping between method names, their overloads and the class that was responsible with logging each.
So, once again, I've avoided digging into an AOP framework. I know what I need to know and when I find the square hole I'll put the square block of AOP into it.