Since learning (and loving) automated testing I have found myself using the dependency injection pattern in almost every project. Is it always appropriate to use this pattern when working with automated testing? Are there any situations were you should avoid using dependency injection?
|
|
Basically, dependency injection makes some (usually but not always valid) assumptions about the nature of your objects. If those are wrong, DI may not be the best solution:
|
|||||||
|
And don't use Service locator as dependency injection alternative, it's anti-pattern, more info: http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx |
|||
|
|
|
Outside of dependency-injection frameworks, dependency injection (via constructor injection or setter injection) is very nearly a zero-sum game: you decrease the coupling between object A and it's dependency B, but now any object that needs an instance of A must now also construct object B. You've slightly reduced the coupling between A and B, but reduced A's encapsulation, and increased coupling between A and any class that must construct an instance of A, by coupling them to A's dependencies as well. So dependency injection (without a framework) is about equally harmful as it is helpful. The extra cost is often easily justifiable, however: if the client code knows more about how to construct the dependency than the object itself does, then dependency injection really does reduce coupling; for example, a Scanner doesn't know much about how to obtain or construct an input stream to parse input from, or what source the client code wants to parse input from, so constructor injection of an input stream is the obvious solution. Testing is another justification, in order to be able to use mock dependencies. That should mean adding an extra constructor used for testing only that allows dependencies to be injected: if you instead change your constructors to always require dependencies to be injected, suddenly, you have to know about your dependencies' dependencies' dependencies in order to construct your direct dependencies, and you can't get any work done. It can be helpful, but you should definitely ask yourself for each dependency, is the testing benefit worth the cost, and am I really going to want to mock this dependency while testing? When a dependency-injection framework is added, and the construction of dependencies is delegated not to client code but instead to the framework, the cost/benefit analysis changes greatly. In a dependency-injection framework, the burden of choosing the right dependencies typically returns to the programmer of the dependent class, by giving appropriate annotations to indicate where the dependency should be sourced from. The ability to circumvent those instructions when it's beneficial for testing is just gravy. I don't see any reason to want to avoid dependency injection if using such a framework. |
|||||||||
|
|
When you don't stand to gain anything by making your project maintainable and testable. Seriously, I love IoC and DI in general, and I'd say that 98% of the time I will use that pattern without fail. It's especially important in a multi-user environment, where you code can be reused again and again by different team members and different projects, as it separates logic from implementation. Logging is a prime example of this, an ILog interface injected into a class is a thousand times more maintainable than simply plugging in your logging framework-du-jour, as you have no guarantee another project will use the same logging framework (if it uses one at all!). However, there are times when it is not an applicable pattern. For example, functional entry points that are implemented in a static context by a non-overridable initialiser (WebMethods, I'm looking at you, but your Main() method in your Program class is another example) simply cannot have dependencies injected at initialisation time. I'd also go as far as to say that a prototype, or any throw-away investigative piece of code, is also a bad candidate; the benefits of DI are pretty much mid-to-long-term benefits (testability and maintainability), if you are certain that you will throw away the majority of a piece of code within a week or so I would say you gain nothing by isolating dependencies, just spend the time you'd normally spend testing and isolating dependencies getting the code working. All in all, it's sensible to take a pragmatic approach to any methodology or pattern, as nothing is applicable 100% of the time. One thing to note is your comment about automated testing: my definition of this is automated functional tests, for example scripted selenium tests if you are in a web context. These are generally completely black-box tests, with no need to know about the inner workings of the code. If you were referring to Unit- or Integration-tests I'd say that the DI pattern is almost always applicable to any project that heavily relies on that kind of white-box testing, since, for example, it allows you to test things like methods that touch the DB without any need for a DB to be present. |
|||
|
|
|
An alternative to Dependancy Injection is using a Service Locator. A Service Locator is easier to understand, debug, and makes constructing an object simpler especially if you aren't using a DI framework. Service Locators are a good pattern for managing external static dependancies, for instance a database that you would otherwise have to pass into every object in your data access layer. When refactoring legacy code, it is often easier to refactor to a Service Locator than to Dependancy Injection. All you do is replace instantiations with service lookups and then fake out the service in your unit test. However, there are some downsides to the Service Locator. Knowing the depandancies of a class is more difficult because the dependancies are hidden in the class's implementation, not in constructors or setters. And creating two objects that rely on different implementations of the same service is difficult or impossible. TLDR: If your class has static dependancies or you are refactoring legacy code, a Service Locator is arguably better than DI. |
|||||||||||
|
