Uncle Bob’s written a post explaining why he tries to be very sparing with the use of IoC containers. I couldn’t agree more. Overuse of IoC containers can end up with a huge amount of indirection and noise in compared to the value they may provide*.
In my mind, the main benefit of dependency injection is testing and is crucial to being able to do TDD. If you do TDD you want to test one thing at a time (one logical assertion per test). If you find you need another piece of logic which is not the responsibility of the object you’re testing you don’t carry on writing reams of code until your test passes, you create a stub for the piece of functionality, make your test pass and only then consider implementing the stubbed out code.
I’ve always been a fan of poor man’s dependency injection. I can’t find any good examples on t’web so I’ll give an example (in C#). Poor man’s basically means having a default parameterless constructor and an overloaded one which takes the dependencies. The default constructor calls the overloaded one with concrete instances of any dependencies:
public interface IUserPaymentRepository
{
void MakeTransaction(Price amount);
}
public class TrackPaymentActivity
{
private UserPaymentRepository _userPaymentRepository;
public TrackPaymentActivity():this(new UserPaymentRepository())
{
}
public TrackPaymentActivity(IUserPaymentRepository userPaymentRepository)
{
this._userPaymentRepository = userPaymentRepository;
}
public AttemptToPayForTrack()
{
......
_userPaymentRepository.MakeTransaction(trackPrice);
......
}
}
This allows you to call the parameterless constructor in your production code, but the overloaded one from your tests. I guess this is where it gets it’s bad name as test hooks are pretty scorned upon and there’s no denying that’s what’s going on here.
Here’s the thing – in many of the situations where you need to inject a dependency it’s for one piece of behaviour and there’s no decision going on to determine which object you need for that behaviour – you know there’s only one place in your code base that has that functionality. In other words, the dependency is still there! It’s just that by using an IoC container you’ve made it magically look like it’s decoupled. With poor man’s it’s easy to see these dependencies but if you’ve palmed it all of to an IoC you’re gonna end up having no idea what’s using what until you fire up the good ol’ debugger. What’s worse is your code metrics won’t pick it up giving you the impression your code is in a better condition than it actually is.
Of course there is a time and a place for IoC containers. It’s just that it’s probably a lot less than you thought. If there’s a decision to be made at runtime about which type that impliments IUserPaymentRepository should be used or there’s more than one member on the interface (suggesting that it is stateful) then an IoC container would be desirable, otherwise I’m often quite happy being poor.
*I actually don’t have a huge amount of experience with IoC containers, but that’s because like Uncle Bob, I’ve always tried to avoid them until absolutely necessary, however I do have first hand reports (within orgs I’ve worked for) of where IoC abuse has caused very expensive problems.