In the past I have made mentions of the notion of Aspect Orientated Programming (AOP) in regards to reducing the noise that can occur when cross cutting concerns, like logging, invade business logic. Unfortunately most of the posts I have made have been in reference to tools and the assistance they can offer. Such tools like PostSharp, Unity, Castle etc provide some "magic" to eliminate the code clutter. Unfortunately many of the people I talk just do not implement tools like this at the place they work and want a POCO option to deliver such results. Well this is actually simpler than many people realise and also points to the issue of the misunderstanding of delegates, anonymous methods and lambdas ; as well as the huge amount of code reuse they can provide.
Firstly I will show an example of “typical” business code that has a lot of business noise. Secondly the code will be show how it could be written if we were use AOP and later on a clean version that mixes POCO with other forms of AOP
class AopEnabledSampleService : ITransferable //from Wiki
{
void Transfer(Account fromAcc, Account toAcc, int amount)
{
if (fromAcc.getBalance() < amount)
{
throw new InsufficientFundsException();
}
fromAcc.withdraw(amount);
toAcc.deposit(amount);
}
}
class NoAopSampleService : ITransferable
{
private string OP_TRANSFER = "Transfer";
private Database database = new Database();
private Logger systemLog;
void Transfer(Account fromAccount, Account toAccount, int amount)
{
if (!getCurrentUser().canPerform(OP_TRANSFER))
{
throw new SecurityException();
}
if (amount < 0)
{
throw new NegativeTransferException();
}
if (fromAccount.getBalance() < amount)
{
throw new InsufficientFundsException();
}
Transaction tx = database.newTransaction();
try
{
fromAccount.withdraw(amount);
toAccount.deposit(amount);
tx.commit();
systemLog.logOperation(OP_TRANSFER, fromAccount, toAccount, amount);
}
catch (Exception e)
{
tx.rollback();
throw e;
}
}
//...more code
}
It is quite clear that the AOP code is much cleaner to look at however there is a lot that is potentially happening that we do not know about. You have to know that the AOP injection or interception is catering for all of the things that the second example dealt with. This is a fundamental problem with AOP: it is not explicit. This obviously can make it very hard to debug and can be confusing to the developer maintaining the code. One way you can get around this by marking up methods or classes with attributes; this at least gives the user of the code a hint as to what is going on. Many of the AOP providers allow for this. However sometime you are just shifting the noise from inside the method to an attribute. How you deal with this is up to you and your team, however I will later on offer some ideas how to manage this.
What the purpose of this post was is to show how we can achieve the functionality of the verbose code above with reduced noise, yet still be maintainable and somewhat explicit. What we will eventually be using is lambdas to achieve the same functionality. Many .Net Dev's use lambdas on a semi regular basis but many do not know how to write a basic API that uses them or even what is really going on when they are using a lambda. Bare with me now while we have a code school moment and cover methods, delegates, anonymous methods, lambdas (closures will be covered in another post). If you are comfortable with all of these then I don't really know why you are reading this post, you should know how to solve this problem already.
Method
Right we all know what a method is; its a function, something that does something, typically a command or a query. You can pass in parameters and you can get something back from a method. The way we typically use a method is in the named sense i.e. 5.ToString(); we are calling the ToString Method on the integer object 5. The name of the method is “ToString”
Delegate
A delegate is to a method what a class is to an object. A class defines an object as a delegate defines a method. Typically most code will never need to define a delegate for a given method unless it is passing the method around like an object... read that again; you can pass methods around like objects. This is where delegates become powerful and this is where the notion of delegates is often misunderstood and often not even known! We will cover more of this later... but for now here is how you define a delegate and what a method would look like that adheres to a delegate.
public class UsingDelegates
{
public delegate void MyDelegate();
public void Main()
{
UseADelegate(this.MyMethod);
}
private void MyMethod()
{
Console.WriteLine("This is My Method!");
}
private void UseADelegate(MyDelegate myDelegate)
{
Console.WriteLine("Before using my delegate");
myDelegate();
Console.WriteLine("After using my delegate");
}
}
/*Output is:
Before using my delegate
This is My Method!
After using my delegate*/
In this code we expose the public method Main which then calls the UseADelegate method passing in the address of the MyMethod method. Note that the parameter passed in to the UseADelegate method does not have the typical parenthesis associated with the method, that is because we want to pass the method as a delegate, not the returned value of the method; This is hugely significant. You will also notice that the UseADelagate method takes in a variable of type MyDelegate. We have defined MyDelegate as a delegate at the start of the class. When you define a delegate you are defining a signature of a method. The name does not matter (except for readability), the only things that matters are A) whatever can use it must be able to access it (an appropriate accessor) and B) the return type and parameter types are consistent with the methods that you intend to use as the delegate. To me this is similar to classes using interfaces, you don't care what the name of the classes that implements the interface is it just has to implement what the interface says to implement. Delegates are similar, however they are not explicit. A method does not says it implements a delegate in the same way a class says it implements an interface.
The syntax for defining a delegate is
[accessor] delegate [return type] [Custom Delegate Name] ([parameter list]);
e.g. public delegate List<Customers> CustomerFilterDelegate(string filter);
Now any method that returns a list of customers and takes in one string parameter is compliant with this delegate.
Right, now that I have told how to define a delegate I am going to throw a spanner in the works and tell you to never do so... sorry. The reason is that now .Net has given us reusable delegates in the form of Func<> and Action<>. Action specifies a delegate with a return type of void so each of its generic parameters are indicators to the parameters in its signature it is defining. Func is used the same however the last generic argument is the return type.
You can now define any reasonable delegate signature with these two generic delegate types. For example the delegate we defined above would now be used as Func<string,List<Customers>> instead of CustomerFilterDelegate. See Framework Design Guidelines for more info.
example of the above code rewritten to be guideline compliant
public class UsingDelegatesCorrectly
{
public void Main()
{
UseAnAction(this.MyMethod);
}
public void MyMethod()
{
Console.WriteLine("This is My Method!");
}
public void UseAnAction(Action myDelegate)
{
Console.WriteLine("Before using my delegate");
myDelegate();
Console.WriteLine("After using my delegate");
}
}
Anonymous Delegates
An anonymous delegate is a method without a name, i.e. it has a body but no name... hmm. As we have mentioned the name of a method has no relevance to whether it adheres to a delegate definition, it is its signature that counts. Previously we were only using the method name as a effective pointer for the address body. What many people don’t know is that you can create a method body without a name, commonly known as “anonymous methods”, “anonymous delegate” or “inline methods” e.g.:
Action myDelegate = delegate()
{
Console.WriteLine(”Hello, World!”);
};
myDelegate();//writes “Hello, World!” to the console
You can use an anonymous delegate anywhere you would typically use a named delegate, however you define the method at the point you wish to use it. The syntax for defining and anonymous delegate is
var x = delegate([parameter list]){[body of method including the return statement]};
Note that the return type is not declared, it is inferred by the presence and type of the return value in the body of the anonymous method. If there is no return value the delegate is considered to have a return type of void. Below we show how the code above would have been written using anonymous delegates:
public class UsingAnonymousDelegates
{
public void Main()
{
UseADelegate(delegate()
{ Console.WriteLine("This is My Method!"); }
);
}
private void UseADelegate(Action myDelegate)
{
Console.WriteLine("Before using my delegate");
myDelegate();
Console.WriteLine("After using my delegate");
}
}
This case show that we do not have to define a delegate signature (the .Net built in Action type is suitable) and we do not even need to create a named method!
Lambdas
Anonymous delegates were great when they came out, it saved a lot of code rewriting and promoted better code reuse; however it was ugly. The majority of the signature still had to be declared and worst of all it was reasonably easy to write anonymous delegates but almost impossible to read them, making maintenance a PITA.
Introducing Lambdas: Lambdas are exactly the same as anonymous delegates in functionality however they have a very different and more readable syntax. Lambdas basically allow the writer of the code to infer a lot about the method signature without explicitly doing so. The reason this can be done is because often the signature is already defined so the lambda can make use of it. Enough chat, lets see what the previous anonymous delegate would look like as a lambda:
Action myDelegate = () => Console.WriteLine("This is My Method!");
myDelegate();
Ok so not a huge difference; we have dropped the key word "delegate" and added an arrow looking thing. Perhaps I should show something a little more complex. Firstly lets define a more realistic anonymous delegate using the method from the first example:
Action<Account, Account, int> transfer = delegate(Account fromAccount, Account toAccount, int amount)
{
if (fromAccount.getBalance() < amount)
{
throw new InsufficientFundsException();
}
fromAccount.withdraw(amount);
toAccount.deposit(amount);
};
as a lambda:
Action<Account, Account, int> transfer = (fromAccount,toAccount, amount) =>
{
if (fromAccount.getBalance() < amount)
{
throw new InsufficientFundsException();
}
fromAccount.withdraw(amount);
toAccount.deposit(amount);
};
As you can see the method body is the same, it is just the definition of the parameters that is different and that is because the types are inferred. Again this may not seem like much at the moment but the heavily reduced noise has allowed for much more readable framework usage. I would hate to think how my current test would look in RhinoMocks if I was not using lambdas!
Couple of things I should mention:
- when using a lambda expression that takes in no parameters use the empty parameters to signal this is the case e.g. ()=>//method body
- if the method body is a one liner you do not need the curly brackets{}, but you do if there is more than one line!
- You do not need the parameter brackets when defining the parameter name if there is only one parameter, you do if there is more than one
- if the return statement is a single statement without curly braces you do not even need the return key word!
(a) => {
return "bob";
}
can be written as
a => "bob";
Just to keep things consistent here is the Console.WriteLine example using lambdas:
public class UsingLambdas
{
public void Main()
{
UseADelegate(
() =>
Console.WriteLine("This is My Method!")
);
}
private void UseADelegate(Action myDelegate)
{
Console.WriteLine("Before using my delegate");
myDelegate();
Console.WriteLine("After using my delegate");
}
}
Using Delegation to Achieve AOP-like Coding
Alright, the whole point to this post was to show how you can use plain .net without any other libraries to do AOP like activities.
Firstly using Lambdas is not as clean as interception, but it is a lot cleaner than copy and paste (right click inheritance) I see so often. I want to help create better code too so here are some thought on where to use AOP and where to use delegation:
- Use delegation when you want to be specific and and explicit about your intentions (e.g. transactions)
- Use interception/injection based AOP for things are are truly behind the scenes (e.g. logging)
- Use attribute based (i.e. explicit) AOP when you want the developer maintaining your code to know that some aspect is taken care of (e.g. security) but you do not want it polluting the method body
Below is an example of what the first example could look like if using a combination of lambdas and AOP:
public class SampleService : BaseService, ITransferable
{
[SecurityCheck]
void Transfer(Account fromAcc, Account toAcc, int amount)
{
TransactionWrapper(() =>
{
if (fromAcc.getBalance() < amount)
{
throw new InsufficientFundsException();
}
fromAcc.withdraw(amount);
toAcc.deposit(amount);
});
}
}
//BASE CLASS
internal abstract class BaseService
{
protected void TransactionWrapper(Action wrappedDelgate)
{
Transaction tx = database.newTransaction();
try
{
wrappedDelgate();
tx.commit();
}
catch (Exception e)
{
tx.rollback();
throw e;
}
}
}
Note
- The logging is no where to be seen. I personally hate seeing logging code, it should be hidden away. To me it is pure noise. This would have been taken care of by the AOP framework of choice.
- Security is kept a subtle as possible without leaving it off the radar. This is not always possible but if I can I keep it out of the method body and as an attribute.
- The transaction is dealt with by a separate method that takes in a delegate. This method can now be reused allowing any other method to take advantage of the pre existing transaction handling. this can now be pushed into a base class, or if a standard .net transaction is being created, a static method that anything can use.
Personally I like the last example the most. However to implement this it does require a reasonably detailed knowledge of AOP so interception can be done using attributes or not and it does require a basic understanding of delegation. Hopefully this post has helped with the later. Next time you start to see repeated code in your code base think if you could use delegation to clean your code up and star making it more reusable.
Rhys