Our Story

Ceremony

Just Married

Delegates make great container components

by - February 15, 2011

The problem with object oriented languages is that you often have to wrap up single functions in a class even when there’s no real reason for that class to exist. This is especially true for many ‘service’ classes. For example, for a long time I used to have something like this in most of my projects:

public interface IDateProvider
{
DateTime Now();
}
public class DateProvider : IDateProvider
{
public DateTime Now()
{
return DateTime.Now();
}
}





I would register my DateProvider with Windsor like this:




container.Regisgter(
Component.For<IDateProvider>().ImplementedBy<DateProvider>()
);





Being able to set the current time in unit tests is essential if you’re doing any kind of logic that does date computations, and this served me well. But it’s a lot of boiler plate just to get the current date. It also means that I have to build a mock IDateProvider for my unit tests and set stubs on it – more irritating boiler plate.



But C# is also quite a good functional language and Windsor deals with delegates just as easily as it deals with interfaces. These days I no longer have an IDateProvider, but just a simple delegate:




public delegate DateTime Now();





Which I register like this:




container.Register(
Component.For<Now>().Instance(() => DateTime.Now),
);









Now any component that needs to know the current DateTime value simply has a dependency on the Now delegate:




public class MyComponent
{
private readonly Now now;
public MyComponent(Now now)
{
this.now = now;
}
public void DoSomething()
{
var currentTime = now();
....
}
}





And it’s really easy to stub out for unit tests:




var now = new DateTime(2010, 2, 15);
var myComponent = new MyComponent(() => now);
myComponent.DoSomething();





This is also a great way for providing infrastructure services when your infrastructure isn’t IoC container friendly. How about this:




public delegate string CurrentUser();

...

container.Register(
Component.For<CurrentUser>().Instance(() => HttpContext.Current.User.Identity.Name)
);





In some ways delegates are more flexible than interfaces since you don’t have to declare that a particular lambda or method implements the delegate, so long as the signature matches you’re good.



Indeed, if Windsor did currying we could probably remove most of our service classes and simply write our software as static methods. But that’s another blog post ;)

You May Also Like

0 comments