[ACCEPTED]-Understanding the need for a DI framework-dependency-injection

Accepted answer
Score: 17

Dependency injection is a degenerate form 9 of implicit parameter passing, and the purpose is essentially the 8 same, to solve what's called The Configurations Problem:

The configurations 7 problem is to propagate run-time preferences throughout 6 a program, allowing multiple concurrent 5 configuration sets to coexist safely under 4 statically guaranteed separation.

Dependency 3 Injection frameworks compensate for the 2 lack of implicit parameters, Curried functions, and convenient facilities for 1 monads in the language.

Score: 15

I've had the exact same question, and it 32 was answered by this:
Granted, you could 31 do what you've described in "Then you could 30 simply do:..." (let's call that "class A"). However, that 29 would couple class A to HandSaw, or to all 28 dependencies needed from class SawMill. Why 27 should A be coupled to HandSaw - or, if 26 you take a more realistic scenario, why 25 should my business logic be coupled to the 24 JDBC connection implementation needed for 23 the DAO layer?
The solution I proposed then 22 was "then move the dependencies one step 21 further" - ok, so now I've got my view coupled 20 to JDBC connection, where I should only 19 deal with HTML (or Swing, pick your flavor).

The 18 DI framework, configured by an XML (or JavaConfig) solves 17 this by letting you just "get the needed 16 service". You don't care how it's initialized, what 15 it needs to work - you just get the service 14 object and activate it.

Also, you have a 13 misconception there regarding the "plus:" (where 12 you do SawMill springSawMill = (SawMill)context.getBean("sawMill"); springSawMill.run();) - you don't need to get the sawMill 11 bean from the context - the sawMill bean 10 should've been injected into your object 9 (class A) by the DI framework. so instead 8 of ...getBean(...), you'd just go "sawMill.run()", not 7 caring where it came from, who initialized 6 it and how. For all you care, it could go 5 straight to /dev/null, or test output, or 4 real CnC engine... The point is - you don't 3 care. All you care is your tiny little class 2 A which should do what it's contracted to 1 do - activate a saw mill.

Score: 13

Spring has three features that are equally 28 important:

  1. dependency injection
  2. aspect oriented programming
  3. library of framework classes to help with persistence, remoting, web mvc, etc.

I'll agree that it's hard to see 27 an advantage to dependency injection when 26 you compare that to a single call to new. In 25 that case, the latter will certainly look 24 simpler, because it's a single line of code. Spring's 23 configuration will always increase the lines 22 of code, so it's not a winning argument.

It 21 starts looking a lot better when you can 20 take a cross-cutting concern like transactions 19 out of your classes and use aspects to set 18 them up in a declarative way. The comparison 17 to a single "new" call isn't what Spring 16 was created for.

Perhaps the best outcome 15 from using Spring is the way its recommended 14 idiom uses interfaces, layering, and good 13 principles like DRY. It's really just the 12 distillation of object-oriented best practices 11 that Rod Johnson used in his consulting 10 gigs. He found that the code he built up 9 over time helped him make a buck delivering 8 better software for his clients. He summarized 7 his experience in "Expert 1:1 J2EE" and 6 ended up open sourcing the code as Spring.

I'd 5 say buy into the framework to the degree 4 that you think his experience can help you 3 write better code, too.

I don't think you 2 can get the full value of Spring until you 1 combine all three of those features.

Score: 6

Granted, this is a contrieved example, and 7 with more complex object relationships it 6 might be more efficient to stash up an XML 5 file than writing it programmatically, but 4 surely there must be more to it than that?

I 3 think it makes more sense to put the "wiring 2 up" in a configuration file rather than 1 doing it manually in code for several reasons:

  1. The configuration is external to your code.
  2. Changes to the wiring up (to tell your sawmill to use a different instance of Saw) can simply be made to the external (XML) file and do not require changing code, re-compiling, re-deploying, etc.
  3. When you have dozens of classes, and several layers of injection (for example: you have a web Controller class which gets a Service class which contains your business logic, which uses a DAO to obtain Saws from the database, which gets a DataSource injected into it, etc.), manually wiring up the collaborators is tedious and requires a few dozen lines of code that do nothing but wiring up.
  4. This is somewhat less of a clear-cut "benefit", but by having all of the 'wiring up' external to the code, I think it helps reenforce to developers the ideas that are at the core of dependency injection, specifically the idea of coding to the interface, not the implementation. With manual wiring up, it can be easy to slip back into old ways.
Score: 5

I generally don't care about XML or Reflection 8 based DI because, in my use cases, it adds 7 unnecessary complexity. Instead, I usually 6 go for some form of manual DI that, to me, seems 5 more natural and has most of the benefits.

public class SawDI{
    public Saw CreateSaw(){
        return new HandSaw();
    }

    public SawMill CreateSawMill(){
        SawMill mill = new SawMill();
        mill.SetSaw(CreateSaw());
        return mill;
    }
}

// later on

SawDI di = new SawDI();
SawMill mill = di.CreateSawMill();

This 4 means I still centralize the coupling and 3 have all the advantages of that, without 2 the dependency on more complex DI framework 1 or the XML configuration files.

Score: 3

One thing that most (if not all) DI containers/libraries 3 bring you in addition is the possibility 2 to intercept methods for all instances created 1 through DI.

Score: 3

Don't forget one major disadvantage of dependency 7 injection: you loose the ability to easily 6 find out from where something is initialized 5 using Find Usages of your powerful Java 4 IDE. This might be a very serious point 3 if you refactor a lot and want to avoid 2 that the test code is 10 times bigger than 1 the application code.

Score: 2

If you hard-code the inserted class, you 7 need that class be available at compile-time. With 6 a configuration-file you could change the 5 used saw (in your case) at runtime without 4 recompiling and even use a saw taken from 3 a new jar you just placed in the classpath. If 2 it is worth the extra complexity is dependent 1 on the task you have to solve.

Score: 2

Lately there has been very much emphasis 53 on DI frameworks, even so that the DI pattern is being forgotten. DI's 52 principles as summarized by J. B. Rainsberger:

It's simple: make dependencies 51 explicit by requiring collaborators as parameters 50 in the constructor. Repeat until you've 49 pushed all decisions about which objects 48 to create into the entry point. Of course, this 47 only applies to Services (in the DDD 46 sense). Done.

As you've noticed, there is 45 not much difference between configuring 44 the dependencies manually vs. using a framework. With 43 constructor injection, as shown below, your 42 code example would have even less boilerplate 41 and the compiler would force you to provide 40 all required dependencies (and your IDE 39 will probably type them for you).

SawMill sawMill = new SawMill(new HandSaw());
sawMill.run();

A DI framework 38 can reduce the boilerplate of creating factories 37 and wiring the objects together, but at 36 the same time it can also make it harder 35 to find out that where each dependency is 34 coming from - the DI framework's configuration 33 is one more layer of abstraction to dig 32 through, and your IDE might not be able 31 to tell you where a particular constructor 30 is being called from.

An indirect disadvantage 29 of DI frameworks is that they can make wiring 28 the dependencies too easy. When you can't anymore 27 feel pain from having lots of dependencies, you may 26 just keep on adding more dependencies instead 25 of rethinking the design of the application 24 to reduce the coupling between the classes. Wiring 23 the dependencies manually - especially in 22 test code - makes it easier to notice when 21 you have too many dependencies as the test 20 setup becomes longer and it becomes harder 19 to write unit tests.

Some advantages of DI 18 frameworks come from their support of advanced 17 features such as AOP (for example Spring's 16 @Transactional), scoping (though many times 15 scoping with plain code will be enough) and pluggability (if a 14 plugin framework is really needed).

Recently 13 I made an experiment of manual DI vs. framework-based 12 DI. The progress and results are shown as 11 screencasts in Let's Code Dimdwarf episodes 42 through 47. The project in question 10 had a Guice-based plugin system for creating 9 actors, which I then rewrote using manual DI, without 8 Guice. The result was a much simpler and 7 clearer implementation, and only a little 6 bit more boilerplate.

Synopsis: First try using just 5 manual DI (preferably constructor injection). If 4 there becomes lots of boilerplate, try to 3 rethink the design to reduce the dependencies. Use 2 a DI framework if some of its features provide 1 value for you.

Score: 1

It's important to understand that Spring 10 is fundamentally two things, one built on 9 top of the other:

  1. A lightweight DI/IoC framework and the classes to implement that (eg XML application contexts, etc); and
  2. It is a lightweight container.

(2) is the bulk of the 8 Spring code. Basically pick a Java technology 7 and you'll probably find Spring has helper 6 classes for it. This is so you can use 5 ActiveMQ, Sun One MQ or whatever and abstract 4 them being a Spring JmsTemplate and the 3 same goes for data access technologies, Web 2 services, etc.

All of these helpers use (1) to 1 wire them together.

Score: 1

One of the biggest benefits of using Dependency 12 Injection is that it makes it much easier 11 to provide mocks or stubs of a class's dependencies 10 when creating a unit test for that class. This 9 allows you to test the class in isolation 8 without depending on its collaborators.

In 7 your example, there is no way to mock or 6 stub out Saw or SawMill in the class that 5 where those are being instantiated. If 4 Saw and SawMill were set via setters or 3 the constructor, then you could pass in 2 your own mock Saw and mock SawMill when 1 running the unit test.

Score: 0

Spring helps you understand and even encourages 8 DI models. However, I don't believe you 7 have to have Spring.

You can have configuration 6 files in Java which you can debug, refactor 5 and perform code analysis on.

I suggest you 4 put these java configuration files in another 3 package, but they are otherwise equivalent 2 to XML configuration files. You can even 1 load these file dynamically if that's important.

More Related questions