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.
Hi Rob, the problem with poor man’s dependency injection in my experience is it gets very messy when your dependencies have dependencies. For example, a controller that takes a repository that takes a session. The construction get’s pretty chaotic and you have no control over the scope of an instance of a dependency. Using an IoC container will supply dependencies to the entire object graph and allow you to control the life cycle of objects. I tend to inject objects that I consider to be external collaborators, such as services, repositories, etc. In saying that, I have seen IoC usage get out of control, mainly when the container is treated as a service locator. Using an IoC with DI forces you to consider which objects you collaborate with, rather than making direct calls to the container. By carefully naming the interfaces you inject, it is easy to identify the roles and responsibilities of your collaborators.
Hi Tim!
Your example of a Repository pattern implimentation sounds like a situation which justifies use of an IoC (however if you’re having to pass around lots of dependencies between collaborators it can often point to a code smell).
My point is not that you shouldn’t use IoC containers, just that you should think very carefully before you do and not be afraid of using poor man’s DI when it’s the more appropriate solution.
Dependency Inversion Principle, really, should be the important principle that we should care about.
Dependency Injection is merely a simple technique that is built around limitations of platforms based around statically typed languages.
Poor Man Dependency Injection like you’re doing is detrimental, I think, to understanding what a class really does. Ideally I (not considering inheritance) should only have to check its public members to gain such knowledge, here by having a public default ctor, anyone using this class will have too too check this default ctor and therefore its implementation details.
And as Tim mentioned, IoC can indeed hide a lot of complexity with large object graphs. And to that I’d say that if the business focused part (ie, not the technical parts) of your system is that complex then you need to split it and use for instance messaging to avoid the Big Ball Of Mud syndrome.
Daniel
*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.
That rings a bell and it’s painful to both my ears.
I like the Poor Man’s IoC and whenever I stumble upon legacy code that has a lot of dependencies I’m tempted to refactor it to use this way of IoC. It’s an easy way to have testable code, and you get to good results very often. Unfortunatly I’ve come upon many places where this isn’t used and where is no room for refactorings, so I have to live with the untestable code or twist myself in various ways to get to a meaningful test.
i feel that having to add code to production to facilitate testing eg extra constructor is not good practise, and granted this might be due to the tools at hand – but sometimes you have to question is this the best tool for the job? ‘poor mans’ highlights something is better than nothing so there is technical success here but how do measure business success with the maintenance issues that will follow down the line? Nice blog.
Hi! nice article, I couldn’t agree more with you. Sometimes overusing something ( like patterns, Ioc containers) can end killing simplicity and legibility. Using poor mans dependency injection maintains that code legibility and you can always inject dependencies by the constructor!