<< Professional Prejudice | Home | Locked In >>

Dependency Injection

posted @ Tuesday, August 07, 2007 7:08 PM

Dependency Injection is a design pattern used to abstract a provider from the class using it. Specifically, the calling class assumes responsibility for managing the provider instead of the class being called. Data access is a classic example of a provider that can be injected into classes that use it. If you decide to implement the DI pattern in a data access project, the most common method of doing so is to add an interface parameter on the constructor of each class that needs data access.

A C# Example

A class that accesses data might look like this (if it were programmed by chimpanzees. Or for an illustrative example.)

Original Orders

I’m using the provider factory to pull the connection details from the application configuration file but otherwise this is pretty standard. Getting the data would require creating the object and calling GetData.

CallOrders
Dependency Injection

Dependency Injection would have me change the base class so that I pass an interface into the constructor. In this case, I can probably choose whether I inject the DbProviderFactory or DbConnection, but since DbConnection already has a handy interface, that’s definitely the best way to go:

DIOrders

The benefit to this pattern is that the class is now disconnected from the data provider. The disadvantage is that now my calling code has to handle the provider.

DICaller

Dependency Injection Dissected

Functionally speaking there’s not really that big a difference between the original version and the one with the dependency injected. In all, the actual complexity of the code is a wash in my opinion.

Either way, I’m better off with an abstracted data provider (as handled in the factory classes). Either way, I need to make sure the project is consistent in how it creates the access providers (I’d probably create an internal utility class with a static method to pull down the connection, so don’t let the extra lines above distract you).

That said, dependency injection doesn’t scale well and creates opportunity for future versioning pain should you find you later need to add providers. What if, for example, I need two data connections instead of one for my class because some of my data is being pulled from a second data source. I would then need two parameters on my constructor. I could move the injection point to the methods instead of the constructor so that only the affected methods have the signature change (a not-uncommon DI method if only a small number of methods have the dependency), but generally that just pushes my problem down to where it affects more code.

And yeah, providers shouldn’t change often and there shouldn’t be more than one or two so this isn’t a huge concern. I just hate breaking encapsulation and even a small hit to your orthogonality can be significant if you find you ever have to change it.

Dependency Injection’s Natural Domain

The real reason that DI has become so popular lately, however, has nothing to do with orthogonality, encapsulation, or other "purely" architectural concerns. The real reason that so many developers are using DI is to facilitate Unit Testing using mock objects. Talk around it all you want to, but this is the factor that actually convinces bright developers to prefer DI over simpler implementations.

I do wish that people would admit that DI doesn’t have compelling applicability outside of Unit Testing, however. I’m reading articles and discussions lately that seem to take the superiority of DI for granted. And I’ve read mock object examples that seem a little bit condescending about dependency injection--as if your projects should already incorporate this technique. I wonder if this is a defensive reaction because the authors of the examples don’t want to have to justify making you fundamentally alter your coding habits and re-write all of your existing code just so that you can use their pet objects.

Superior .Net Mocking

All of this becomes particularly exasperating in the dead silence regarding TypeMock. I blogged about this product as soon as I found it because their claims are simply stunning. TypeMock claims to allow you to mock objects used by your classes without having to expose those internal objects at all.

I have completed a project using TypeMock for my Unit Testing and I can verify that they are correct.

No interface creation or injection required. No dependency injection at all. No change to how I want to create my objects. No orthogonality issues hidden like a time-bomb in my code, however tiny. TypeMock uses reflection to intercept pesky calls and has robust assert, parameter, return object and verify options. The ability to select which calls to mock and which to let the original objects handle is an outstanding unlooked-for bonus.

Best of all, TypeMock is free to use. Yeah, it’s easier to use their professional and/or enterprise products because there are improved ease-of-programming objects in those editions, but their community edition has all of the actual functionality and none of the cost.

What Gives?

So tell me; what gives? Why the resounding silence around this tool? If it works, why isn’t every .Net developer on the planet talking about it. If it doesn’t why aren’t they reviling TypeMock for taunting us so cruelly? I’m serious. Why isn’t this sucker taking the Unit Testing space by storm? And why am I still hearing about the virtues of a pattern whose sole perceptible benefit is allowing mock objects in Unit Tests?

Comments

  1. Nate Kohari

    Posted on: 8/8/2007 10:37 AM

    # re: Dependency Injection

    While it's certainly useful for unit testing, dependency injection -- or to be more exact, inversion of control, the idea behind DI -- can have dramatic benefits on your code if you use it often. Relying on dependency injection helps to organize your application into highly-cohesive, loosely-coupled units that can be reused easily.

    You are correct, though, that dependency injection by hand gets cumbersome quickly. The real benefits of DI appear when you use a framework (sometimes called an "inversion of control container") to support it. When you request an instance of a type, a DI framework can build an entire object graph, wiring up dependencies as it goes. When I first was introduced to the idea of dependency injection, I didn't see the merit, but one day it just clicked and I saw the light. :)

    Check out Castle Windsor (http://castleproject.org/), or my own project, Ninject (http://ninject.org/). They're both different takes on frameworks that can help with DI.

    (Also, TypeMock is nice, but check out Ayende Rahien's Rhino.Mocks. It seems to be widely considered the "best" mocking library for .NET.)

  2. Jacob

    Posted on: 8/8/2007 12:57 PM

    # re: Dependency Injection

    Nate,

    I've heard the party line. I just don't buy it. How can you say that dependency injection (I'm not taking on the whole inversion of control pattern, but I might. Jury's still out on that one.) creates loosely coupled units that can be reused easily when the whole point of DI is to require the caller to provide the callee's needs? That's an increase in coupling by any reasonable assessment. Dumping the coupling workload onto a framework doesn't change the fact that it's still a requirement to supply external stuff to use an object. If it's external to both objects, but only one object requires it to function, then only one object should have any knowledge of it. Yeah, that's kind of prehistorically OO of me, but I still haven't seen a compelling case to extend the coupling.

    Thanks for the link to Castle's Windsor stuff. I can see the attraction. It makes DI easier by allowing you to define the dependencies and resources in a config file and provides common interfaces for managing DI. What I don't see is why you'd prefer an Inversion of Control pattern over a much simpler provider pattern? Because that's really the benefit you're seeing here--an embedded provider pattern in Castle Windsor (and I suspect other IoC containers). That's why I used a provider for the data access in my example (so that you could see the specifically DI wrinkles separate from the advantage of using a provider). A provider framework sounds a lot more useful to me than an IoC container.

    You've seen the light so help me see the light here. I don't need a testimonial, I need a logical path. What light did you see? How does DI actually help you that wouldn't have been possible any other way?

    I've checked out Rhino.Mocks. Ayende is a bright guy. I like his blog. That's why it's on my blogroll on the right. But Rhino Mocks requires Dependency Injection. Ugh. I wish Ayende would admit that the only advantage to DI in his examples is to allow the mock objects to work, is all. There is no other compelling reason.

    Finally, I don't care how many people consider a given tool to be the best. I'm the one deciding which tool to use and I want that decision to have a rational basis. Tell me how Rhino.Mocks is better than TypeMock. Why on earth should I choose a tool that requires DI over one that doesn't? That's a big hurdle and one that is going to require a tool to bring more than popularity to the table to make its case.

  3. Nate Kohari

    Posted on: 8/15/2007 9:08 AM

    # re: Dependency Injection

    Sorry for the delay in responding... it become a post unto itself. :)

    http://kohari.org/2007/08/15/defending-dependency-injection/

  4. Jacob

    Posted on: 8/15/2007 9:55 AM

    # re: Dependency Injection

    Sweet. Good post. You need a better example, though, because in the car case I consider the engine to be a property that shouldn't be buried in the first place.

  5. ulu

    Posted on: 8/29/2007 2:34 PM

    # re: Dependency Injection

    In your example, the Orders class has too many responsibilities. Database access, reading configuration, and perhaps some business logic as well. So, regardless of testability or flexibility, DI lets you have a simple class that is just concerned about your business logic. Now, I'm all for TypeMock, don't get me wrong. The difference is, while Rhino forces you to use DI (and thus "better" design), TypeMock gives you freedom to design whatever you think is best. You decide for yourself.

  6. Jacob

    Posted on: 8/29/2007 4:42 PM

    # re: Dependency Injection

    @ulu: Yeah, as I said, "I’d probably create an internal utility class with a static method to pull down the connection, so don’t let the extra lines above distract you." It's not a great way to separate concerns, but it works well enough for most small to medium sized projects.

    That said, I refined my take on Dependency Injection in some of the follow-up posts if you want to explore what I mean.

  7. Paulio

    Posted on: 3/22/2008 4:36 AM

    # re: Dependency Injection

    I have to say that I agree with Jacab. I think we're missing the point of coupled code. The factory and DI mechanisms are both ways to leave the decisions about which concrete class to use as late as possible - often via a config file. That's a good thing but IMO both have the same adv. and disadvantages. To reuse the car example, with a config file I can examine some aspect of the call (a param in the factory or constructor in DI) and create a Honda with a Vtec engine. However, I'm still tightly coupled to the abstract data type. If I change the base class/interface then all my clients still need to change. You cannot simply sweep coupling by version under the carpet. IMO the DI is worse than a factory. When I create a model of a domain problem that is it. With DI I have to alter the model to allow for the DI plumbing. With a helper pattern such a factory I don't have to compromise my model at all.

    Read more of my views on

    http://www.jameskovacs.com/blog/CommentView.aspx?guid=B3BC3324-E3FA-4AE0-8323-0E276E351C66

  8. Arturas

    Posted on: 5/12/2008 6:34 AM

    # re: Dependency Injection

    I'd suggest to check upon winter4net - our fast, compact, scalable, spring compatible .NET Inversion of Control (IoC, dependency injection) container.

Your comment:



 (will not be displayed)


  Please add 7 and 8 and type the answer here: