Rejecting Dependency Injection Inversion
Uncle Bob has a post about why you should limit your use of IoC containers. I read that post with something very close to trepidation, because the first example that I saw told me a lot about the underlying assumptions made when this post was written.
Just to give you an idea about how many problems there are with this example when you want to talk about IoC in general, I made a small (albeit incomplete) list:
- The example is a class that has two dependencies, who themselves has no dependencies.
- There is manual mapping between services and their implementations.
- All services share the same life span.
- The container is used using the Service Locator pattern.
Now, moving to the concrete parts of the post, I mostly agree that this is an anti pattern, but not because of the code is using IoC. The code is actually misusing it quite badly, and trying to draw conclusions about the practice of IoC from that sample (or similar to that) is like saying that we should abolish SQL because of an example using string concatenation has security issues.
I am not really sure about the practices of IoC usage in the Java side, but on the .NET world, that sort of code is frowned upon for at least 4 or 5 years. The .Net IoC community has been very loud about how you should use an IoC. We have been saying for a long time that the appropriate place to get instances from the IoC is deep in the bowels of the application infrastructure. A good example of that is using ASP.Net MVC Controller Factory, that is the only place in the application that will make use of the container directly.
Now, that takes care of the direct dependency on the container, let us talk about a dependency graph that has more than a single level to it. Here is something that is still fairly simplistic:
I colored all the things that share the same instance and those that do not. Trying to keep track of those manually, or through factories, would be a pure nightmare. Just try to imagine just how much code you are going to need to do that.
Furthermore, what about when we have different life spans for different components (logger is singleton, database is per request, tracking service is per session, etc). At this point you raise the complexity of the hand rolled solution by an order of magnitude once again. Using an IoC, on the other hand, means that you just need to configure things properly.
Which leads me to the next issue, manually mapping between services and their implementation is something that we more or less stopped doing circa 2006. All containers in the .Net space supports some form of auto registration, which means that usually we don’t have to do anything to get things working.
As I said, I am not really sure what the status is on the Java world, but I have to say that while the issues that Uncle Bob pointed out in the post are real, the root cause isn’t the use of IoC, it is the example he was working with. And if this is a typical example of IoC usage in the Java world, then he should peek over the fence to see how IoC is commonly implemented in the .Net space.
Comments
Thanks for this very interesting point of view! I usually really like Uncle Bobs writings and I'm surprised that he mixes up the limitations of concrete implementations in the java world and the limitations of an abstract concept.
What readings would you recommend to somebody to get started with DI in the .Net world? My feeling is, that there is a lot of information available, which leads you in the wrong direction as their technics are implementation/language dependent. Also there are many DI implementations in .Net and you have to get started with one. But how to choose one if you are not already an DI expert?
cheers,
Achim
Hi Ayende,
Thank for your post. The case you mentioned above is exactly like my case. I'm using Unity as the IOC library. Since i don't want to make my Service implementation dirty with [Dependency] attribute from Unity, I decided to use Contructor injection. However, following this approach, the bootstrapper becomes a huge class with thousand lines of injection code for service classes. Personally, I think It's really the main drawback when using IOC
Thoai,
They you have a problem in the way you are using IoC, period.
Most commonly, I have about 10 lines to setup IoC for an entire project
Thoại, nobody orders you to put all the IoC setup into single class... Maybe the main drawback sits between the table and the chair?
Thoai,
I've found that Unity is more the limitation in this sense. It doesn't go as far as I would like (in my opinion) and almost refuse to call it an IoC Container. Using something like windsor enables more of a mindset change as it supports not only auto registration (which is why you are experiencing the pain you are having) but also the lifestyle management and facilities open up so many more possibilities.
When unity is the only exposure to IoC I've just not seen them 'get' the benefits of IoC and ultimately it ends up being a mess. Of course, ymmv but I would strongly suggest giving something like windsor a go and see what possibilities it opens up.
I have to confess, I'm stuck in the "confused by IOC" camp. I keep hearing IOC veterans talking about how easy it is, how if it's hard that's because you're doing it wrong -- but then they never go on to demonstrate, with substantial code examples, how to do it right. Where are the good implementations, that use IOC the right way, in nontrivial real-world apps? A pointer to two or three of those would be worth a hundred blog posts saying "Uncle Bob is wrong".
We only use constructor injection and use StructureMap to auto wire it up. All we need is:
Scan(x => {
});
DefaultConventions ties up the interface with its concrete class, eg ICustomerService to CustomerService. Then we only need to be specific when the lifecycle of the instance is different.
Uncle Bob tried using the Straw Man Argument:
http://en.wikipedia.org/wiki/Straw_man
Ayende, you mention that we stopped manually mapping between services and their implementation in 2006. Could you please elaborate on what you mean by this. Are you actually talking about manually writing xml config file for a given container ie. Castle Windsor? If so, how else would you do it? I find attributes a little invasive but would use them if I had to. I also struggle to see how you would request instances from a given container without the use of a factory vis a vis auto registration.
Gavin,
Take a look here: http://ayende.com/Blog/category/451.aspx
That is just one example of how to implement convention over configuration for a container. You setup the parameters, and then you are pretty much done with it
Jonty, you don't even need that as StructureMap will scan all assemblies with a single line on of code.
x.LookForRegistries();
Greg,
another example from StructureMap:
Scan(s =>
<siteregistry();
<iaction().NameBy(t =>
means, register all types implementing IAction with a name that is derived from the type's name with a certain convention.
A good IoC may also help you in separating your conventions from your wiring code:
Scan(s=>
<siteregistry();
<httphandlerregistrar();
HttpHandlerRegistrar is a convention for finding IHttpHandler implementations and name it with a relative URL provided via attribute. Such conventions can be reused, which can dramatically reduce your wiring code.
And I forgot to escape the < and >, damn :(
It says e.g. "s.AddAllTypesOf<IAction>() "
and "s.With<HttpHandlerRegistrar>"
sorry!
Thanks for your reply, I am so totally going to be using the castle batch registration facility from now on!
I write applications like that all the time, and I don't see the complexity you are speaking of. I just create the logger and settings objects at the top level and pass them down to whatever components need it. It can be tedious at times to thread everything through, but it isn't the slightest bit complex.
Dependency Injection only makes sense when you truly don't know what the concrete dependencies are until runtime. For example, generating menus and the pages/forms they link to at run time. For stuff that is never, ever going to change like your logging component or how you access databases, there is just no point to it.
Jonathon,
I have to disagree. Probably the single biggest benefit to DI is in testability. When testing, being able to replace your logging component or database access object is key.
Thanks,
Bradley
Bradly is right...
Regarding the registration, not sure about all the other container but both Windsor and StructureMap will allow you to register your entire application with a few lines of codes with a fluent interface :)
@Jonathan - but why even pass such objects down all the time in all the places when you can set up an IoC convention once and only once and then have it taken care of everywhere? I haven't instantiated or passed down a dependency in close to two years now, and I was late to the IoC game, in terms of getting it into a client project that is. ;)
For the record, I also haven't instantiated a factory for dependencies or even written any IoC registration code in just as long, as the automatic registration convention I implemented takes care of that too. I just write new public interfaces that implement my IComponent interface, write new implementations, and then consume them in the ctor arguments of other component implementations. The whole thing gets wired up automatically based on the conventions I established long ago. Ya gotta love low-friction development!
Uncle Bob did not write that code. It comes from the tutorial on Google Guice, the IOC used in the example.
code.google.com/.../
This happens in big companies all the time. Just remember the MVC sample applications from Microsoft.
Stephen Oakman :
"I've found that Unity is more the limitation in this sense. It doesn't go as far as I would like (in my opinion) and almost refuse to call it an IoC Container"
Agreed and technically I'd say it actually ISN'T a container. It's a container-like facade over a factory (which incidentlly is somehting the Unity guys would agree with). For instance, a container "contains" things you put in it. If you don't put something in it you can't get it out of the container. In Unity if you don't put something in it first you still can get it out! This is really because of the architectural mistake of not actually keeping a mapping of registered types on the container but deffering everything to the underlying ObjectBuilder system.
Don't believe the above? Here's a fun expiriment: Create a UnityContainer and ask for an Object from it. Guess what? You'll get one.
This is the cause of innumerable unity configuration based bugs than I can tell you. Item not in the container? Throw and exception or return null or something - ANYTHING than make me think it's actually been set up right. [rolls eyes]
I've learned to take what Uncle Bob says with a grain of salt. :) As Ayende points out, his argument is based on a very bad example.
For others looking to explore the benefits of IOC Containers, a good starting point is ensuring they understand the fundamental concept of Inversion of Control.
Start writing code where you are providing dependencies to instances of classes instead of "new"ing them inline, or even using Factories to construct them. This can be by passing them in a constructor, or setting up properties. The key beneffit of IOC as Bradley mentioned, is testability. Once your code is interacting with what it is provided, instead of creating instances of its dependencies, you can substitute those dependencies with Mocks and Stubs to test your code atomically. Provided you also follow good programming fundamentals such as Liskov substitution principle (LSP) it makes your code much easier to modify later on by substituting in new implementations that take advantage of different technologies, etc. (I.e. substituting a logger that logs to file with one that supports event log or DB.)
Once this "clicks", then start experimenting with IOC Containers to detect and automatically provide those dependencies. Two good IOC containers to get started with are StructureMap or Autofac.
I agree with Jonathan (although I wouldn't "pass loggers down"): DI is often abused, leading to pointless complexity and code...
Testability shouldn't be an excuse to use DI, since how "testable" your code is depends on the mocking tool you use. In the Java world, we have free (and open source) tools that make writing unit tests for any kind of code easy.
Java has had a solid dependency injection framework for years with Spring, but now that has been standardized with CDI (JBoss weld is the reference imlementation, and JBoss Seam provides further extension.) I don't think that misuse is the norm, just as other have said, it's possible to misuse anything.
CDI/Weld is great because it's part of the Java EE framework as a standard, meaning one less place where configuration is required - complexity goes down.
With great power comes great responsibility.
"CDI/Weld is great because it's part of the Java EE framework as a standard, meaning one less place where configuration is required - complexity goes down."
For those who do not know: CDI is JSR 299 Contexts and Dependency Injection for the JavaTM EE platform.
When are we going to get a community .NET specification thingy going? It's great the amount of frameworks for DI in .NET, but they are reinventing the wheel all of them. I think it's good to get behind a common spec.
Comment preview