views:

1604

answers:

14

I'm trying to introduce DI as a pattern here at work and one of our lead developers would like to know: What - if any - are the downsides to using the Dependency Injection pattern?

Note I'm looking here for an - if possible - exhaustive list, not a subjective discussion on the topic.


Clarification: I'm talking about the Dependency Injection pattern (see this article by Martin Fowler), not a specific framework, whether XML-based (such as Spring) or code-based (such as Guice), or "self-rolled".

+17  A: 

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
Epaga
Why it takes time to learn? I thought it makes things simple compared with ejb.
fastcodejava
+1, although I don't agree on the latter bullet...
Mark Seemann
This seems hand-wavy considering you don't want a subjective discussion. I suggest building (collectively, if necessary) a realistic example for examination. Building a sample *without* DI would be a good starting point. Then we could challenge it w.r.t. to requirement changes and examine the impact. (This is extremely convenient for me to suggest, by the way, as I'm about to go to bed.)
Jesse Millikan
This really needs some examples to back it up, and having taught dozens of people DI I can tell you that it really is a very simple concept to learn and to start putting into practice.
chillitom
The only thing I'd say is that it has taken time to learn how to think "in DI" - it's a different (and better) way of approaching design. No longer do I let classes create and fetch their own dependencies. The object tree is built from leaf to root rather than vice versa. That takes a little getting used to. That's what I meant with taking time to learn.
Epaga
I don't really consider DI a pattern. It (coupled with IoC) are really more of a programming model - if you fully follow them, your code looks a lot more Actor-like than "typical" pseudo-procedural OO.
kyoryu
A: 

Code readability. You'll not be able to easily figure out the code flow since the dependencies are hidden in XML files.

Rahul
You don't need to use XML configuration files for dependency injection - choose a DI container which supports configuration in code.
Håvard S
Dependency injection doesn't necessarily imply XML files. It seems like this may still be the case in Java, but in .NET we abandonded this coupling years ago.
Mark Seemann
Or don't use a DI container at all.
Jesse Millikan
if you know the code is using DI, you can easily assume who sets the dependencies.
Bozho
+26  A: 

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 incure 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.

Håvard S
- Separation of classes reduces complexity. Many classes don't make an application complex. - Your should only have a dependency at to your DI framework at the application-root.
Robert
We're humans; our short term memory is limited and cannot handle many <xxx> simultaneously. It's the same for classes as it is for methods, functions, files, or any construct you use to develop your program.
Håvard S
@Havard S: Yes, but 5 really complex classes is not simpler than 15 simple ones. The way to decrease complexity is to reduce the working set required by the programmer working on the code - complex, highly interdependent classes do not accomplish that.
kyoryu
@kyoryu I think we all agree on that. Keep in mind that _complexity is not the same as coupling_. Decreasing coupling can increase complexity. I'm really not saying that DI is a bad thing because it increases the number of classes, I'm highlighting the potential downsides associated with it. :)
Håvard S
basically it decreases the complexity of each individual building block, but increases the number of building blocks used to build the wall.
Epaga
@Havard S: Fair 'nuff. Part of it is also how you define complexity - I generally define it as the minimum working set required by the programmer.
kyoryu
+1 for "DI increases complexity" That's true in DI and beyond. (Almost) any time we increase flexibility, we're going to increase complexity. It's all about balance, which begins with knowing the upsides and downsides. When people say, 'there are no downsides,' it's a sure indicator that they haven't fully understood the thing yet.
Don Branson
Yeah. Today I ditched a major feature in about five minutes, since all I had to do was delete a few classes and modify my object graph. Since no actual logic was changed, it was really easy to do with high confidence, and the compiler errors from the missing object told me what needed changing. Man, that complexity sure bit me in the ass!
kyoryu
To build on what @Robert said, DI removes a little complexity in the form of wiring dependencies from many classes and centralizes it. It also makes every test less complex. The new place that does wiring may be somewhat complex but overall DI should greatly reduce complexity. If you think that DI increases complexity, you're probably doing it wrong.
Craig P. Motlin
+4  A: 

If you have a home-grown solution, the dependencies are right in your face in the constructor. Or maybe as method parameters which again is not too hard to spot. Though framework managed dependencies, if taken to the extremes, can begin to appear like magic.

However, having too many dependencies in too many classes is a clear sign that you're class structure is screwed up. So in a way dependency injection (home-grown or framework managed) can help bring glaring design issues out that might otherwise be hidden lurking in the dark.


To illustrate the second point better, here's an excerpt from this article (original source) which I whole heartedly believe is the fundamental problem in building any system, not just computer systems.

Suppose you want to design a college campus. You must delegate some of the design to the students and professors, otherwise the Physics building won't work well for the physics people. No architect knows enough about about what physics people need to do it all themselves. But you can't delegate the design of every room to its occupants, because then you'll get a giant pile of rubble.

How can you distribute responsibility for design through all levels of a large hierarchy, while still maintaining consistency and harmony of overall design? This is the architectural design problem Alexander is trying to solve, but it's also a fundamental problem of computer systems development.

Does DI solve this problem? No. But it does help you see clearly if you're trying to delegate the responsibility of designing every room to its occupants.

Anurag
The second paragraph is very true
Bart van Heukelom
+5  A: 

Like everything else, it is no silver bullet!

fastcodejava
but it is close to being one.
Bozho
@Bozho - every silver lining has its cloud. If IoC fixes all of your problems then you don't have very interesting problems. It's good stuff and is great at promoting loose coupling and single responsibility, but it is a bit dangerous to think that everything will get better once you put in IoC.
Mike Two
DI doesn't solve all problems. It makes them easier to solve.
Bozho
Nice one-liners, Bozho.
Epaga
+45  A: 

It can be dangerous for your career because it may increase your overall knowledge of good API design. Once you learn how proper loosely coupled code can look like, it may turn out that you will have to decline lots of job offers because you would otherwise have to work with tightly coupled legacy apps. Happens to me a lot :)

Mark Seemann
Even in your current job it can seriously destroy your motivation to see yourself become better and better, but the better part of the code base remains the same big pile of mud and your co-workers are happy to wallow in it or have given up.
DR
@DR: That is so true. It's even worse when people don't get what a benefit it is - "Look, I was able to add this major feature without changing a single line of existing code"
kyoryu
Yes, a common problem. I found that you can mitigate it a bit by taking the codebase as a challenge of its own, and systematically refactor it. Still needs some support from your boss, but can be quite rewarding. I once cut a class from 8,000 to 5,000 lines, with the same functionality :-).
sleske
@sleske - you can achieve better compression than that in the time it takes to launch 7-zip. The real question is, has the time you invested in that change proved worthwhile? How much time has that effort saved for you and others? How much value has that effort contributed to the business, relative to what you could have been doing?
Steve314
@sleske - of course I understand that's hard to quantify - I just have architecture astronaut leanings myself, so I've learned to ask those questions.
Steve314
@Steve314: Yes, I realize it's a tradeoff. In my experience, judicious refactoring pays off in reduced maintenance cost and less bugs. It may not make sense for parts of the code that are not changed any more.
sleske
+1, though I have to admit that since the question wasn't limited to *constructor injection* I thought you might mention how *a certain form of dependency injection* is terrible! (http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx) ;)
Jeff Sternal
@sleske - a 5kloc class? You should consider a little more refactoring my friend, small classes FTW.
chillitom
@chillitom: I know, and i do. It's just that I also need to produce some results in my job, apart from refactoring...
sleske
+8  A: 

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

Gabriel Ščerbák
why was my answer downvoted?
Gabriel Ščerbák
I am curious, when someone asks for wonsides, why answers in a spirit "there aren't any" get upvoted and those which contain some information related to the question not?
Gabriel Ščerbák
those are all interesting links with valid points
Anurag
-1 because I'm not looking for a link collection, I'm looking for actual answers: how about summarizing each article's points? Then I'd upvote instead. Note that the articles themselves are quite interesting though it seems the first two are actually debunking negatives of DI?
Epaga
I wonder if the most upvoted answer got downvoted as well, because it really doesn't answer the question at all imho:)Sure I know what you asked and what I answered, I am not asking for upvotes, I am just confused why my answer isn't helping whilst apparently saying DI downside is that it is too cool is helping much.
Gabriel Ščerbák
The most upvoted answer is just plain funny, that's why I didn't downvote it. Feel free to if you want to. It IS anonymous after all. ;-)
Epaga
No, I find it funny too, but I am not thinking about downvoting questions which aren't that funny and try to point to potential answers:)
Gabriel Ščerbák
...nah answers not question...
Gabriel Ščerbák
there ya go. :)
Epaga
+2  A: 

It can increase app startup time because IoC container should resolve dependencies in a proper way and it sometimes requires to make several iterations.

Roman
Just to give a figure, a DI container should resolve multiple thousands dependencies per second. (http://www.codinginstinct.com/2008/05/ioc-container-benchmark-rerevisted.html) DI containers allow deferred instantiation. Performance should (even in huge applications) not be a problem. Or at least performance problem should be addressable and not be a reason to decide against IoC and its frameworks.
Robert
+3  A: 

If you're using DI without an IOC container, the biggest downside is you quickly see how many dependencies your code actually has and how tightly coupled everything really is. ("But I thought it was a good design!") The natural progression is to move towards an IOC container which can take a little bit of time to learn and implement (not nearly as bad as the WPF learning curve, but it's not free either). The final downside is some developers will begin to write honest to goodness unit tests and it will take them time to figure it out. Devs who could previously crank something out in half a day will suddenly spend two days trying to figure out how to mock all of their dependencies.

Similar to Mark Seemann's answer, the bottom line is that you spend time becoming a better developer rather than hacking bits of code together and tossing it out the door/into production. Which would your business rather have? Only you can answer that.

Mike Post
+10  A: 

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.

kyoryu
But surely that "downside" is because of the nature of the problem we are solving, decoupling it so we can easily change the implementation means there is no one place to look at, and what relevance is it being able to look at it? The only case I can think is in debugging and the debugging environment should be able to step into the implementation.
vickirk
This is why the word "downside" was in quotes :) Loose coupling and strong encapsulation preclude "one place to see everything," kind of by definition. See my comments on Havard S's response, if you get the feeling that I'm against DI/IoC.
kyoryu
@kyoryu I agree (I even voted you up). I was just making the point.
vickirk
+3  A: 

I find that constructor injection can lead to big ugly constructors, (and I use it throughout my codebase - perhaps my objects are too granular?). Also, sometimes with constructor injection I end up with horrible circular dependencies (although this is very rare), so you may find yourself having to have some kind of ready state lifecycle with several rounds of dependency injection in a more complex system.

However, I favour construtor injection over setter injection because once my object is constructed, then I know without a doubt what state it is in, whether it is in a unit test environment or loaded up in some IOC container. Which, in a roundabout sort of way, is saying what I feel is the main drawback with setter injection.

(as a sidenote, I do find the whole topic quite "religious", but your mileage will vary with the level of technical zealotry in your dev team!)

James B
If you have big ugly constructors, may be your classes are to big and you just have to much dependencies?
Robert
That is certainly a possibility I'm willing to entertain!...Sadly, I don't have a team I can do peer reviews with though. *violins play softly in the background, and then screen fades to black...*
James B
As Mark Seeman sad above "It can be dangerous for your career" ...
Robert
@James, I had no clue background narrations could be so expressive here :P
Anurag
+1  A: 

DI is a technique or a pattern and not related to any framework. You can wire up your dependencies manually. DI helps you with SR (Single responsibility) and SoC (separation of concerns). DI leads to a better design. From my point of view and experience there are no downsides. Like with any other pattern you can get it wrong or misuse it (but what is in the case of DI quite hard).

If you introduce DI as principle to a legacy application, using a framework - the single biggest mistake you can do is to misuse it as a Service-Locater. DI+Framework itself is great and just made things better everywhere I saw it! From organizational standpoint, there are the common problems with every new process, technique, pattern, ...:

  • You have to train you team
  • You have to change your application (which include risks)

In general you have to invest time and money, beside that, there a no downsides, really!

Robert
+13  A: 

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.

EDIT - A couple of relevant links...

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

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

Steve314
+4  A: 

This is more of a nitpick. But one of the downsides of dependency injection is that it makes it a little harder for development tools to reason about and navigate code.

Specifically, if you Control-Click/Command-Click on a method invocation in code, it'll take you to the method declaration on an interface instead of the concrete implementation.

This is really more of a downside of loosely coupled code (code that's designed by interface), and applies even if you don't use dependency injection (i.e., even if you simply use factories). But the advent of dependency injection is what really encouraged loosely coupled code to the masses, so I thought I'd mention it.

Also, the benefits of loosely coupled code far outweigh this, thus I call it a nitpick. Though I've worked long enough to know that this is the sort of push-back you may get if you attempt to introduce dependency injection.

In fact, I'd venture to guess that for every "downside" you can find for dependency injection, you'll find many upsides that far outweigh it.

Jack Leow
Ctrl-shift-B with resharper takes you the implementation
adrianm
But then again would we not (in an ideal world) have at least 2 implementations of everything, i.e. at least one real implementation and a mock one for unit testing ;-)
vickirk