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:
First, most basically, DI assumes that tight coupling of object implementations is ALWAYS bad. This is the essence of the Dependency Inversion Principle: "a dependency should never be made upon a concretion; only upon an abstraction".
This closes the dependent object to change based on a change to the concrete implementation; a class depending upon ConsoleWriter specifically will need to change if output needs to go to a file instead, but if the class were dependent only on an IWriter exposing a Write() method, we can replace the ConsoleWriter currently being used with a FileWriter and our dependent class wouldn't know the difference (Liskhov Substitution Principle).
However, a design can NEVER be closed to all types of change; if the design of the IWriter interface itself changes, to add a parameter to Write(), an extra code object (the IWriter interface) must now be changed, on top of the implementation object/method and its usage(s). If changes in the actual interface are more likely than changes to the implementation of said interface, loose coupling (and DI-ing loosely-coupled dependencies) can cause more problems than it solves.
Second, and corollary, DI assumes that the dependent class is NEVER a good place to create a dependency. This goes to the Single Responsibility Principle; if you have code which creates a dependency and also uses it, then there are two reasons the dependent class may have to change (a change to the usage OR the implementation), violating SRP.
However, again, adding layers of indirection for DI can be a solution to a problem that doesn't exist; if it is logical to encapsulate logic in a dependency, but that logic is the only such implementation of a dependency, then it is more painful to code the loosely-coupled resolution of the dependency (injection, service location, factory) than it would be to just use
newand forget about it.
Lastly, DI by its nature centralizes knowledge of all dependencies AND their implementations. This increases the number of references that the assembly which performs the injection must have, and in most cases does NOT reduce the number of references required by actual dependent classes' assemblies.
SOMETHING, SOMEWHERE, must have knowledge of the dependent, the dependency interface, and the dependency implementation in order to "connect the dots" and satisfy that dependency. DI tends to place all that knowledge at a very high level, either in an IoC container, or in the code that creates "main" objects such as the main form or Controller which must hydrate (or provide factory methods for) the dependencies. This can put a lot of necessarily tightly-coupled code and a lot of assembly references at high levels of your app, which only needs this knowledge in order to "hide" it from the actual dependent classes (which from a very basic perspective is the best place to have this knowledge; where it's used).
It also normally doesn't remove said references from lower down in code; a dependent must still reference the library containing the interface for its dependency, which is in one of three places:
- all in a single "Interfaces" assembly that becomes very application-centric,
- each one alongside the primary implementation(s), removing the advantage of not having to recompile dependents when dependencies change, or
- one or two apiece in highly-cohesive assemblies, which bloats the assembly count, dramatically increases "full build" times and decreases application performance.
All of this, again to solve a problem in places where there may be none.
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 tradeoffs are a bit different; what you're losing by injecting a dependency is the ability to know easily what implementation you are relying on, and shifting the responsibility for deciding what dependency you are relying on to some automated resolution process (e.g. if we require an @Inject'ed Foo, there must be something that @Provides Foo, and whose injected dependencies are available), or to some high-level configuration file that prescribes what provider should be used for each resource, or to some hybrid of the two (for example, there may be an automated resolution process for dependencies that can be overridden, if necessary, using a configuration file).
As in constructor injection, I think the advantage of doing so ends up, again, being very similar to the cost of doing so: you don't have to know who is providing the data you rely on, and, if there are multiple potential providers, you don't have to know the preferred order to check for providers in, make sure that every location that needs the data checks for all potential providers, etc., because all of that is handled at a high level by the dependency injection platform.
While I don't personally have a great deal of experience with DI frameworks, my impression is that they provide more benefit than cost when the headache of finding the correct provider of the data or service that you need has a higher cost than the headache, when something fails, of not immediately knowing locally what code provided the bad data that caused a later failure in your code.
In some cases, other patterns that obscure dependencies (e.g. service locators) had already been adopted (and perhaps also proven their worth) when DI frameworks appeared on the scene, and the DI frameworks were adopted because they offered some competitive advantage, such as requiring less boilerplate code, or potentially doing less to obscure the provider of dependency when it becomes necessary to determine what provider is actually in use.
if you are creating database entities, you should rather have some factory class which you will inject instead to your controller,
if you need to create primitive objects like ints or longs. Also you should create "by hand" most of the standard library objects like dates, guids, etc.
if you would like to inject configuration strings it's probably better idea to inject some configuration objects (in general it is recommended to wrap simple types into meaningful objects: int temperatureInCelsiusDegrees -> CelciusDeegree temperature)
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
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.
While the other answers focus on the technical aspects I would like to add a practical dimension.
Over the years I came to a conclusion that there are several practical requirements that need to be met if introducing Dependency Injection is to be a success.
There should be a reason to introduce it.
This sounds obvious, but if your code only gets things from the database and returns it without any logic then adding a DI container makes things more complex with not real benefit. Integration testing would be more important here.
The team needs to be trained and on board.
Unless the majority of the team is on board and understands DI adding inversion of control container becomes a yet another way to do things and made the code base even more complicated.
If the DI is introduced by a new member of the team, because they understand it and like it and just want to show that they are good, AND the team is not actively involved, there is a real risk that it will actually decrease quality of the code.
You need to test
While decoupling is generally a good thing, DI can move resolution of a dependency from compile time to run time. This is actually quite dangerous if you don't test well. Run time resolution failures can be costly to track and resolve.
(It is clear from your test that you do, but many teams don't test to the extent that is required by DI.)
Try to use basic OOP principles: use inheritance to extract common functionality, encapsulate (hide) things, that should be protected from outside world using private/internal/protected members/types. Use any powerful test-framework to inject code for tests only, for example https://www.typemock.com/ or https://www.telerik.com/products/mocking.aspx .
Then try to re-write it with DI, and compare code, what you will see usually with DI:
- You have more interfaces (more types)
- You've created duplicates of signatures of public methods and you will have to double mantain it (you can't simply change some parameter once, you will have to do it twice, basically all refactoring and navigational possibilities becoming more complicated)
- You've moved some compilation errors to run time failures (with DI you can just ignore some dependency during coding and not for sure it will be exposed during testing)
- You've opened your encapsulation. Now protected members, internal types etc became public
- Overall amount of code was increased
I would say from what I saw, almost always, code quality is being decreased with DI.
However, if you use only "public" access modifier in class declaration, and/or public/private modifiers for members, and/or you don't have a choice to buy expensive test tools and same time you need unit testing that can't be replaced by integrational testing, and/or you already have interfaces for classes you are thinking to inject, DI is a good choice!
p.s. probably I will get a lot of minuses for this post, I believe because most of modern developers just don't understand how and why to use internal keyword and how to decrease coupling of your components and finally why to decrease it) finally, just try to code and compare
An alternative to Dependency 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 dependencies, 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 Dependency 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 dependencies 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 dependencies or you are refactoring legacy code, a Service Locator is arguably better than DI.