Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the downsides to using Dependency Injection? [closed]

People also ask

What are the pros and cons of using dependency injection?

Primary advantage: encourages attention to the Dependency Inversion Principle, which helps reduce the overall cost of changing the system. Primary disadvantage: highlights design problems and causes many programmers to blame dependency injection for the design problems.

Is dependency injection really necessary?

The dependency injection technique enables you to improve this even further. It provides a way to separate the creation of an object from its usage. By doing that, you can replace a dependency without changing any code and it also reduces the boilerplate code in your business logic.

Is dependency injection an overkill?

If you have a really small project with 12 classes, then a DI framework is almost certainly overkill. As a rule of thumb, the point where it becomes truly useful is when you find yourself repeatedly writing code that wires up object graphs with multiple dependencies and have to think about where to put that code.


A couple of points:

  • DI increases complexity, usually by increasing the number of classes since responsibilities are separated more, which is not always beneficial
  • Your code will be (somewhat) coupled to the dependency injection framework you use (or more generally how you decide to implement the DI pattern)
  • DI containers or approaches that perform type resolving generally incur a slight runtime penalty (very negligible, but it's there)

Generally, the benefit of decoupling makes each task simpler to read and understand, but increases the complexity of orchestrating the more complex tasks.


The same basic problem you often get with object oriented programming, style rules and just about everything else. It's possible - very common, in fact - to do too much abstraction, and to add too much indirection, and to generally apply good techniques excessively and in the wrong places.

Every pattern or other construct you apply brings complexity. Abstraction and indirection scatter information around, sometimes moving irrelevant detail out of the way, but equally sometimes making it harder to understand exactly what's happening. Every rule you apply brings inflexibility, ruling out options that might just be the best approach.

The point is to write code that does the job and is robust, readable and maintainable. You are a software developer - not an ivory tower builder.

Relevant Links

http://thedailywtf.com/Articles/The_Inner-Platform_Effect.aspx

http://www.joelonsoftware.com/articles/fog0000000018.html


Probably the simplest form of dependency injection (don't laugh) is a parameter. The dependent code is dependent on data, and that data is injected in by the means of passing the parameter.

Yes, it's silly and it doesn't address the object-oriented point of dependency injection, but a functional programmer will tell you that (if you have first class functions) this is the only kind of dependency injection you need. The point here is to take a trivial example, and show the potential problems.

Lets take this simple traditional function - C++ syntax isn't significant here, but I have to spell it somehow...

void Say_Hello_World ()
{
  std::cout << "Hello World" << std::endl;
}

I have a dependency I want to extract out and inject - the text "Hello World". Easy enough...

void Say_Something (const char *p_text)
{
  std::cout << p_text << std::endl;
}

How is that more inflexible than the original? Well, what if I decide that the output should be unicode. I probably want to switch from std::cout to std::wcout. But that means my strings then have to be of wchar_t, not of char. Either every caller has to be changed, or (more reasonably), the old implementation gets replaced with an adaptor that translates the string and calls the new implementation.

That's maintenance work right there that wouldn't be needed if we'd kept the original.

And if it seems trivial, take a look at this real-world function from the Win32 API...

http://msdn.microsoft.com/en-us/library/ms632680%28v=vs.85%29.aspx

That's 12 "dependencies" to deal with. For example, if screen resolutions get really huge, maybe we'll need 64-bit co-ordinate values - and another version of CreateWindowEx. And yes, there's already an older version still hanging around, that presumably gets mapped to the newer version behind the scenes...

http://msdn.microsoft.com/en-us/library/ms632679%28v=vs.85%29.aspx

Those "dependencies" aren't just a problem for the original developer - everyone who uses that interface has to look up what the dependencies are, how they are specified, and what they mean, and work out what to do for their application. This is where the words "sensible defaults" can make life much simpler.

Object-oriented dependency injection is no different in principle. Writing a class is an overhead, both in source-code text and in developer time, and if that class is written to supply dependencies according to some dependent objects specifications, then the dependent object is locked into supporting that interface, even if there's a need to replace the implementation of that object.

None of this should be read as claiming that dependency injection is bad - far from it. But any good technique can be applied excessively and in the wrong place. Just as not every string needs to be extracted out and turned into a parameter, not every low-level behaviour needs to be extracted out from high-level objects and turned into an injectable dependency.


Here's my own initial reaction: Basically the same downsides of any pattern.

  • it takes time to learn
  • if misunderstood it can lead to more harm than good
  • if taken to an extreme it can be more work than would justify the benefit

The biggest "downside" to Inversion of Control (not quite DI, but close enough) is that it tends to remove having a single point to look at an overview of an algorithm. That's basically what happens when you have decoupled code, though - the ability to look in one place is an artifact of tight coupling.


I have been using Guice (Java DI framework) extensively for the past 6 months. While overall I think it is great (especially from a testing perspective), there are certain downsides. Most notably:

  • Code can become harder to understand. Dependency injection can be used in very... creative... ways. For example I just came across some code that used a custom annotation to inject a certain IOStreams (eg: @Server1Stream, @Server2Stream). While this does work, and I'll admit has a certain elegance, it makes understanding the Guice injections a prerequisite to understanding the code.
  • Higher learning curve when learning project. This is related to point 1. In order to understand how a project that uses dependency injection works, you need to understand both the dependency injection pattern and the specific framework. When I started at my current job I spent quite a few confused hours groking what Guice was doing behind the scenes.
  • Constructors become large. Although this can be largely resolved with a default constructor or a factory.
  • Errors can be obfuscated. My most recent example of this was I had a collision on 2 flag names. Guice swallowed the error silently and one of my flags wasn't initialized.
  • Errors are pushed to run-time. If you configure your Guice module incorrectly (circular reference, bad binding, ...) most of the errors are not uncovered during compile-time. Instead, the errors are exposed when the program is actually run.

Now that I've complained. Let me say that I will continue to (willingly) use Guice in my current project and most likely my next. Dependency injection is a great and incredibly powerful pattern. But it definitely can be confusing and you will almost certainly spend some time cursing at whatever dependency injection framework you choose.

Also, I agree with other posters that dependency injection can be overused.


I don't think such a list exists, however try to read those articles:

  • DI can obscure the code (if you're not working with a good IDE)

  • Misusing IoC can lead to bad code according to Uncle Bob.

  • Need to look out for over-engineering and creating unnecessary versatility.