Saturday, January 8, 2011

Dependency Injection != using a DI container

What is the relationship between Dependency Injection and Dependency Injection containers?  Is a Dependency Injection container required to do Dependency Injection?  Is using a DI container the same thing as doing DI?  These were some of the questions that I had when I first began trying to apply Dependency Injection.  Most of the sources that I found at the time treated Dependency Injection as being synonymous with the use of a Dependency Injection container, as if they were inseparable and a container was a prerequisite for doing DI.  This article is my explanation of the relationship between Dependency Injection and Dependency Injection containers (also known as DI frameworks and IoC containers) for those who might be wondering about these issues now (in the context of static and mildly dynamic languages).

Dependency Injection and Dependency Injection containers are two completely independent concepts.  You can do Dependency Injection without a Dependency Injection container, and you can use a Dependency Injection container without doing Dependency Injection.  Here’s how I classify the combination of values:

No DI ContainerDI Container
No DISpaghetti CodeService Locator Anti-Pattern
DIManual Dependency InjectionEnhanced Dependency Injection

Let’s take a look at each quadrant:

Spaghetti Code (No DI & no DI container used): Consider the spaghetti code metaphor for a moment.   What does a metaphoric strand of spaghetti represent in software system?  A spaghetti strand is a unit of code [which likely serves as a dependency for some other unit(s) of code].  So spaghetti code then is where many units of code (i.e. dependencies) are intertwined throughout the system and are difficult to separate from the whole (just list spaghetti strands on a plate of spaghetti).  The system is highly coupled, and it’s difficult to change a single unit of code without changes rippling  throughout the system.  So why do I refer to code written without Dependency Injection as spaghetti code?  Without DI, construction (and lifetime) concerns are mixed with application logic concerns.  As a result, most units are likely to be highly coupled with several other units.  Often the coupling is so high that nearly everything is transitively dependent on nearly everything else.  DI is the antidote for highly-coupled code.

[Update: My use of the spaghetti code metaphor was not without controversy.  For further discussion of this issue, see this follow-up post.]

Service Locator Anti-Pattern (DI container used without DI):  As Mark Seeman notes, Service Locator is an anti-pattern.  A Service Locator causes a class to, as Misko Hevery says, lie about its dependencies.  The use of a Service Locator is the “look, don’t ask” school of design rather than Dependency Injection’s “ask, don’t look” philosophy.  In other words, with Dependency Injection, a component “asks” for its dependencies (typically by requiring them in the constructor) rather than “looking” for them (such as by querying a Service Locator / Registry / Context).  Service Locators make for a confusing experience for the client.  Just say no to the Service Locator abomination.

Manual Dependency Injection (DI used without a DI container):  DI at its core is about creating loosely coupled code by separating construction logic from application logic.  This is done by pushing creation of services to the entry point(s) and writing the application logic so that dependencies are provided for its components.  The application logic doesn’t know or care how it is supplied with its dependencies; it just requires them and therefore receives them.  The ‘Manual’ in the name means that dependency creation isn’t automatically handled.  In other words, you have to write code to instantiate the dependencies.

Enhanced Dependency Injection (DI using a DI container): The point to realize here is that Enhanced Dependency Injection is still Dependency Injection.  The important part is the “Dependency Injection” part, not the “Enhanced” part.  The enhancement is that the dependencies are automatically resolved (i.e. created) for you.  That is, you don’t have to write code to instantiate the dependencies. If you’re doing Dependency Injection right, whether you are doing Manual Dependency Injection or Enhanced Dependency Injection, almost all of your code looks *exactly* the same, differing only at the entry point(s).  References to your container should be very isolated, occurring only near the entry points.  The entry points are the only places that should be aware that you are using a container.  Everything else just gets supplied the required dependencies and doesn’t know or care where they came from.  Let me emphasize: a container shouldn’t affect the structure, design or implementation of your code except in a few isolated, well-defined places.  In other words, whether or not you are using a container is irrelevant for nearly all of your code.   A container does indeed buy you some convenience and some additional functionality.  But there’s no tool that can make your code Dependency Injection ‘compliant’; that is up to your design of the classes and their relationships, and that is the important part.

Getting started with Dependency Injection

My recommendation is to start with Manual Dependency Injection.  Forget about the tools (that is, DI containers).  Learn the basics, and understand the technique.   Learn Dependency Injection in a containerless way first.  As a side-benefit, when you work in a world where containers are not very prevalent (such as PHP, C++ or Objective-C), you’ll have no problem creating a loosely-coupled system by applying Manual DI.

Once you get how to apply the technique, then take a look at what a tool can do for you.  You’ll be able to apply to use the tools much more effectively by understanding the technique first.  I’m not knocking DI containers but simply stating that to make effective use of any tool, you have to know how and when to apply it.

When combined with good OO design and an understanding of what to inject and what not to inject, Dependency Injection is the best technique that I know to produce loosely-coupled, testable software.  Learn how to create loosely coupled systems with Manual Dependency Injection.  Don’t let a tool blind you to the technique.

21 comments:

  1. Hi I have a question that's a bit off topic, it concerns "Service Locator Anti-Pattern (DI container used without DI)". I read Seeman's post and I agree that it is a real problem to use a DI Container in this manner. DI Containers are for DI. However, I do sometimes use the Container in a simialr manner in situations like this.
    public void DoSomething (string input, string someValue){
    var myService = ServiceLocator.Get().Get.ByName(input);
    myService.Process(someValue);
    }
    This is how I use the command pattern rather then a giant switch or if else statement. However, a) it uses strings which we all know suck, and b) it breaks runtime rather then compiletime so it needs defensive coding. Is there a better way of doing this?
    Thanks,
    Raif

    ReplyDelete
  2. @Raif - It's very difficult to make a suggestion without understanding more about the context of your question.

    It's convenient to define things in terms of strings (very flexible) but also not very explicit. When feasible, I tend to favor explicitness over convenience. Or more correctly, in the medium-to-long run (and, usually, in the short run also), I believe that explicitness *is* convenience. But sometimes all we get is strings (e.g. HTTP request params) and have to use them at the entry points as strings (and hopefully convert them into more useful forms on the way down). Adding explicitness also often helps with modeling by helping to reveal domain concepts that may not have been as obvious previously in the less-structured context.

    Your DoSomething() method is basically using the Service Locator as a service factory. My first impression is that you may want to consider moving the service factory up to a higher level (closer to the entry point) and then just directly calling service.Process(someValue) at that point. I'm not sure there's much value added by having DoSomething() wrap the service factory.

    ReplyDelete
  3. Service Location is bad, but not as bad as people make out.

    The two issues with it raised in the article you linked to require special situations. The first is that consumers find it hard to know what to inject - so if your dll isn't for 3rd party consumption, it isn't necessarily a no-no*

    The second issue raised - the developer doesn't know if the depenency is registered in the container or not is a problem with constructor injection too, so not that great a counter to service location.

    Note I haven't said Service Locators rock. The thing is, sometimes you have to use them e.g. in code instantiated by 3rd party frameworks, like your ORM layer or web framework where the framework doesn't have hooks for your container.

    ReplyDelete
  4. Spot on! Including the containers and calling DI a form of Inversion of Control (as they do at wikipedia) is really obscuring the view. I had a very similar eureca moment after reading Hevery: Dependency Injection - some updates and Dependency Injection or Removing Hardcoded Values? but you worded it a lot more eloquently :)

    ReplyDelete
  5. @Harry,

    I've never met a Service Locator that I liked. It makes the code harder to test, and harder for clients in general to use since the dependencies are hidden.

    I agree with you though that sometimes they are unavoidable when using 3rd party code that written without testability in mind.

    ReplyDelete
  6. Honestly the thing that i don't agree with is the NO DI / No Dependancy injection case where you say you have spagetti code.

    You can just use singletons/factories that are really some hand coded service locator implementation.

    This change nothing to your graph of dependancies.

    ReplyDelete
  7. @Foudres -

    Yes, the 'spaghetti code' label wasn't entirely well-received. It was intended as a succinct summary of the mixing of concerns that occurs without DI rather than as a pejorative. I hope to have a new post soon to discuss this further.

    However, I don't agree that singletons (in the Gang of Four sense) and service location don't change the dependency graph. GoF singletons introduce a dependency on global state, and service location of course introduces a dependency between a client and its collaborator. Factories can also introduce additional dependencies but are also entirely appropriate in places (such as when a collaborator's lifetime is less than that of the client).

    But, sure, it's entirely possible to write good code without DI. People have been doing that for a long time. I would argue that it's a little more difficult, though (due to the mixing of concerns). And the use of singletons and service location can of course be done in a manageable fashion, but I prefer to not have a need to manage that effort.

    And of course, it's entirely possible to write spaghetti code with DI too (especially without an understanding of what should be injected and what should not be).

    ReplyDelete
  8. You might also want to try Ding (http://marcelog.github.com/Ding), a dependency injection and inversion of control container for PHP 5.3, modeled after Spring(tm) for Java. Supports JSR 250 and 330 annotations, xml and yaml as bean definitions, has AOP and a MVC implementation.

    Regards!

    ReplyDelete
  9. Great post. I think I need some more work on DI before I jump into DICs. Can you expand a bit on what those "few, well-defined areas" would be for DICs in a Zend Framework project?

    ReplyDelete
  10. Hi Jeremy,

    Unfortunately I can't offer much help with Zend Framework as I haven't used it. However, my understanding is that it was written without testability in mind (to some extent, at least) - for example, a reliance on Singletons and global state in other forms. The developers seem aware of the issues, and I believe they are working on addressing these issues.

    So I think that currently you'll probably have trouble using a container/factory only at the entry point.

    My advice for the general case of using a difficult-to-test framework is to try to define a clear boundary between the problematic third party code and your own clean code. For example, perhaps you can inject your dependencies by referencing your factory/container in the controller factories or in the controllers (you would essentially be using a Service Locator), and then be able to write all of your code beyond that boundary testably and using DI. I wouldn't ever advise using Service Location in a greenfield project, but when a bridge is needed to existing untestable code, sometimes such solutions are worth it if they allow for the rest of the system to be developed cleanly (as long as the Service Locator usage is strictly limited to these areas - these would serve as your "few, well-defined areas" in this case). But again, this is general advice, so the particular details may not apply well to the Zend Framework.

    If you're just getting started, I would suggest looking at another post of mine about how to apply DI effectively http://loosecouplings.blogspot.com/2011/01/how-to-write-testable-code-overview.html. When getting started, it helps to have an understanding of what to inject, what not to inject, and helpful ways to structure your code .

    ReplyDelete
  11. Sorry, this is wrong. It is *not* possible to do dependency injection without using a container. Martin Fowler coined the term dependency injection, you can read his article here:

    http://martinfowler.com/articles/injection.html

    Dependency injection is a specific type of inversion of control. The core principle of inversion of control is "don't call us, we'll call you" - ie, the framework calls your code, rather than you calling the framework. In the case of dependency injection, the framework calls your code to supply the dependencies.

    regards,
    Jordan.

    ReplyDelete
  12. Jordan - I disagree. No container/framework is needed. DI can be done just fine using a manual factory class (or classes if you have multiple scopes). All that is needed for dependency injection is a third-party that binds clients with their collaborators, and a custom factory class can be that third party. If you consider a custom, non-generic factory class to be a DI container, then I agree with you, but that would miss the point of comparing and contrasting the four different approaches.

    Manual DI is entirely consistent with the type of IoC associated with DI. DI is about inverting creation control. In other words, creation control is inverted because clients no longer create their own dependencies. Whether the third party binder is a generic DI container/framework or a custom factory class, the end result (inversion of creation control) is the same.

    Best,
    Loose

    ReplyDelete
  13. Dependency injection specifically refers to the use of a container in order to wire up the dependencies of your application. You cannot get away from this.

    Martin Fowler and members of the Spring and Pico development teams coined the term to specifically mean exactly that.

    You said: "All that is needed for dependency injection is a third-party that binds clients with their collaborators, and a custom factory class can be that third party."

    The factory class does not bind the client with its collaborators. It is simply responsible for creating the collaborator. Is leaves the job of binding to the application code.

    Yes, alot of people were using design patterns such as Factory/AbstractFactory, and using programming techniques such as programming to interfaces is order to write decoupled, cohesive code for many years (decades!) before dependency injection containers came along. This does not mean that they were using dependency injection.

    I admit I am being very picky here, and I do apologise for that.

    The most important thing is that you we all try to write well organised code. How that is achieved that is up to you (DI, Factories, whatever...)

    regards,
    Jordan.

    ReplyDelete
  14. Jordan - Thank you for the thoughtful response. I suspect that perhaps we may agree more than it might seem.

    When I refer to a factory, I'm not referring to any old factory, but an application factory. It essentially serves the role of a DI container. The term 'DI container' to me however means a generic framework intended for use in multiple unrelated contexts whereas an application factory is of course highly specific to a single context [the application]. Perhaps the distinction that I was trying to make would have been clearer if, rather than 'DI container', I used the phrase ‘generic DI container’ or ‘reusable DI container’. The distinction between a generic DI container and an application factory is important to me because I feel that, when it comes to DI, often there is a lot of focus on technology (DI container/framework) and not much on what (to me) DI is all about - patterns for separating creation logic from business logic. Whether the segregated creation functionality is done with a generic DI container or with an application factory is ultimately an implementation detail and (in my opinion) orthogonal to what dependency injection is all about.

    Regarding binding, I feel that a factory/container does (in many cases) bind collaborators to their clients. I may have a Service that is composed with several other Services. Another Service at a higher level of abstraction may then in turn be composed with that Service (as well as perhaps others in addition). And so on until we reach the application object, which the application factory creates. In these instances, there is no distinction between creation and binding.

    Best regards,
    Loose

    ReplyDelete
  15. Hi Loose,

    I agree, my comment about Factories was wrong - I realised that after I posted :)

    And yes by coding in a certain way you can get some of the important benefits of DI.

    But being a stubborn kinda chap, I would like to dig my heals in one more time and say that DI is DI though - you need a container to do it :)

    regards,
    Jordan.

    ReplyDelete
  16. I worked at SpringSource and at the start of teaching the Core Spring Framework course I would introduce Dependency Injection:

    I would show how you can do dependency injection without a framework first - to get the fundamentals of the pattern. Only having done that, would we explore what the container could do for you.

    (I thought I did a pretty good job teaching that course :) )

    Recently, I created a DI container for Objective-C: www.typhoonframework.org



    ReplyDelete
  17. This seems like a strange argument to be having. A "container" in this context is something I'd consider either a bounded / explicit or unbounded / implicit namespace.

    If you declare a bunch of things as static or global objects that are accessible to all units, then you don't need to issue any sort of "lookup" on them -- you just refer to them.

    It can be argued that this establishes a "dependency" that can be abstracted away. However, moving the same bindings into an explicit namespace (a container) that requires lookups based on some kind of key before they can be made just shifts the dependency to the container! In the end, there's always going to be at least one namespace that simply CANNOT be abstracted away.

    I mean ... where/how is the container initialized if you move the declarations out of global namespace? Oh, well, they get moved into a configuration file that's stored on the file system -- a physical dependency. Or perhaps they're moved into records on a back-end database -- a physical and logical dependency -- that's probably also abstracted away once the resources in the container have been initialized.

    You can take as puritanical viewpoint on this as you like. But in the end, it's six of one and half-dozen of the other: you have either a single text file that happens to be source code that defines a bunch of global objects, versus a text file that contains, say, XML meta-data descriptions of the same bunch of objects that are loaded into a dictionary at program startup.

    The meta-data descriptive approach buys you one clear benefit: you can (in theory) reconfigure the program without recompiling it.

    Anybody who's ever configured a Linux server, for example, has had first-hand experience of both sides of this in action. Tons of system parameters are specified in config files that can be changed at any time and the system re-booted to have them take effect. It also compiles different chunks of the software during the installation process in order to ensure system-specific dependencies are hard-coded into the apps for highest performance, since it's known those particulars are not going to change unless certain aspects of the hardware environment change, most of which are highly unlikely to occur.

    ReplyDelete
  18. what do you think of mi little DI container?

    http://code.google.com/p/infectorpp/

    ReplyDelete
  19. I know this is an old post, but could you give me your opinion on this question? http://stackoverflow.com/questions/3823062/how-to-use-dependency-injection-without-breaking-encapsulation?noredirect=1#comment47699574_3823062

    Rodrigo
    rodrigo.ruiz7@gmail.com
    (I didn't want to log in to my Google account from this computer)

    ReplyDelete
  20. Hey man, I am doing a DI presentation and researched this past few days some related concepts as IoC, IoC Containers, DIC, DI, DIP and such, and I really have to say that these is a great article and I really liked it and thought it was really helpful. As developers, we use these concepts on a day to day basis but I see that our definitions are very fuzzy and many things we do, we do them out of intuition: "because that's how it's done". The reason why I want to do the presentation is to clarify these things and get people aware of the needs that generated the concepts and why we use tools such as DICs and DI. I think this article achieves just that. Keep doing a great job!

    ReplyDelete
    Replies
    1. Thank you very much, Calin. I'm glad that you found the post helpful. Cheers!

      Delete