Aspect Oriented Programming with Action
To be honest, I don’t think I’ve ever really seen it successfully implemented. I mean sure, I’ve seen examples of how you could use it for “cross-cutting” concerns like logging.
The problem is it is usually pretty difficult to use, and the only real practical application I can ever come up with is logging. I know, it is probably just my lack of knowledge in the area, but if you bear with me I’ll show you a neat little trick you can use to address cross-cutting concerns by doing something similar to what AOP does using Action<>.
Putting it all in one place
The main problem AOP tries to solve is taking aspects of your software that exist in many different places and condensing them into one place for you to maintain.
Exception handling and logging tend to be the most infamous of these cross-cutting concerns. Many places in your code you no doubt have many instances where you catch an exception and the only thing you can really do is log it.
I’m going to show you a little easy way to do that using Action<>.
Giving credit where credit is due, I got this idea from some code that a coworker of mine, Subha Tarafdar, wrote. (He is a genius.)
He wrote some code to basically do what I am going to show you, but with retrying database queries. He was able to reduce many places in the code base where we had repeated logic to retry executing database queries when getting a deadlock or timeout.
Does this code belong to you?
public void MakeRice()
{
try
{
_riceCooker.Cook();
}
catch (Exception exception)
{
// Don't care if this fails,
// there is nothing we can do about it.
Logger.Log(exception.Message);
}
}
Ignore that I am catching a general exception here. It is a bad practice, but sometimes all you are going to do is log whatever bad thing happens and move on.
It’s pretty common to do something in a try block and catch an exception only to log it.
Think about how many times this code or something similar to it might be sprinkled throughout your code base.
Action<> to the rescue
If you’re not familiar with Action<> take a look at this post I did that gives a very simple explanation for how it works.
We can take the logic of the try, catch, and log exceptions and put it into a method that only varies by what action we do.
public static void LogOnFailure(Action action)
{
try
{
action();
}
catch (Exception exception)
{
Logger.Log(exception.Message);
}
}
I’m not a big fan of static methods but in certain cases they make sense. The alternative is to have all of this code sprinkled throughout your code base.
Now that we have this method, we can do anything we want and know that if there is an error it will be logged.
Check this out:
LogOnFailure(_riceCooker.Cook);
LogOnFailure(KickACat);
LogOnFailure(() =>
{
Wakeup();
SmellTheRoses();
}
);
And if you decide you want to change how you log the error or what you do on it, you can change it all in one place.
Not just for logging
You can apply this kind of solution in many places where you have cross cutting concerns in your code base.
Here are a few suggestions for places you might consider this kind of a solution:
- Retrying on failure logic
- Using an alternative service for a failure (web service “A” failed, but we can use web service “B”)
- Database connection and connection closing logic. (Open connection, do something, close connection.)
As always, you can subscribe to this RSS feed to follow my posts on Making the Complex Simple. Feel free to check out ElegantCode.com where I post about the topic of writing elegant code about once a week. Also, you can follow me on twitter here.
References: Aspect Oriented Programming with Action<> from our NCG partner John Sonmez at the Making the Complex Simple blog.