Rob Ashton recently posted that he doesn't like using IoC containers. I've written about Ioc Containers a fair bit. I like Rob's post, even though I'm unlikely to follow his advice. I like Ioc Containers. True, it's possible to overdo them. It's possible to end up with symptoms that Rob mentioned, like:
-Lots of little objects lacking any cohesion
- Deep and complex object dependency graphs
- "Concrete abstractions" – i.e. interfaces that have only 1 implementation
- Complex container config with special cases and injected wrappers
- Multiple levels of factories, indirection via interfaces, i.e. "Interfaces via interfaces to interfaces"
Liz Keogh tweeted recently: "realised *all* tools can be misused to replace the thing they were meant to help with. E.g. powerpoint for presenting. It's not just BDD" I'd add, likewise, an IoC container is meant to help with OO design, not replace it.
Rob has a point that you can do DI and IoC without an IoC container. And in fact, not having a container will hold you back from some of the worst excesses of container-fiddling. And containers might encourage you to "run before you walk" in DI and IoC practices. When starting with a new tool, the tendency is to try to use it deeply, and some of those uses will, in time, end up being seen as antipatterns. Especially the more complex and non-obvious ones. Hopefully we get through this phase and end up with a good usage pattern. Rob's post makes me feel better about not using the advanced features of various IoC containers (nested subcontainers? Nope, me neither.)
It's also very possible to write code the way that Rob is describing – using Dependency Injection, Inversion of Control but no container allowed. But for the majority of developers who have never used an IoC Container, this style is totally unknown and telling them to ignore the lessons from IoC containers would be their loss. You can view the container as training wheels (a way of enforcing discipline) towards that enlightenment if you like.
At it's best an IoC Container is an engine for separating concerns - we didn't usually think about singletonhood as a separate concern before IoC containers made it plain and easy to achieve. I believe it is possible to use an IoC container as knife to factor up legacy codebases, separating it out into testable parts along the seams. You can do it incrementally. And it's handy to use an IoC container to bootstrap the application at a known extension point - for instance the controller factory in ASP.Net MVC.
Here's one thing to look out for. In strongly typed OO languages like C# or Java we make interfaces when:
- There is, in the program, more than one implementation of the methods on the interface, but we don't want to use the first choice of a classical OO class hierarchy with method overrides in subclasses.
- We want to depend on the abstraction without taking on board the dependencies of the implementation of that abstraction.
- There is more than one implementation of the methods on the interface, but the secondary implementation is for testing – it is a dummy, mock, fake, etc.
A rule of thumb, if there is only one implementation of an interface, and the implementing class can be isolated from external dependencies (databases, web services, file systems) entirely by injecting mock dependencies, then that interface is probably not needed and should be removed.
Here's another thing to look out for: Beware of statement that "This kind of app benefits from x, but this app is to small to need it, we'll add it when we do" (where x is unit tests, CI, IoC container, a message queue, DAL, an ORM, etc...) – apps grow, and there's a persistent project growth antipattern that plays out like this:
1) The project starts small, and you don't need the tools yet. Even though adding them at or near the start is fairly trivial.
2) Later you become aware of pain due to the "small" project having grown, unnoticed, past the point of benefiting from the tool or practice.
3) It's easier to make the next increment in the way that the last one was done rather than refactor to fit in the tool. So it doesn't happen.
4) It would be disruptive now to refactor the existing codebase's architecture to the pattern that's needed. Now you're stuck in the hard place.
There's a sweet spot between YAGNI and "Yes, you are going to need it". A team that inists on strict YAGNI for production code might insist on CI and a working tests with the first line of code ... before it's needed. Because it will be needed and it's a lot easier to do it upfront. With IoC containers specifically, yes they are quite useful in moderately-sized apps. And the overhead is minimal.