views:

1335

answers:

12

This might be a naive question. I'm currently learning the Spring framework and dependency injection. While the basic principle of DI is rather easy to grasp, it's not immediately obvious why you need an elaborate framework to implement it.

Consider the following:

public abstract class Saw
{
    public abstract void cut(String wood);
}

public class HandSaw extends Saw
{
    public void cut(String wood)
    {
        // chop it up
    }
}

public class ChainSaw extends Saw
{
    public void cut(String wood)
    {
        // chop it a lot faster
    }
}

public class SawMill
{
    private Saw saw;

    public void setSaw(Saw saw)
    {
        this.saw = saw;
    }

    public void run(String wood)
    {
        saw.cut("some wood");
    }
}

Then you could simply do:

Saw saw = new HandSaw();
SawMill sawMill = new SawMill();
sawMill.setSaw(saw);
sawMill.run();

Which would be equivalent to:

<bean id="saw" class="HandSaw"/>

<bean id="sawMill" class="SawMill">
   <property name="saw" ref="saw"/>
</bean>

plus:

ApplicationContext context = new ClassPathXmlApplicationContext("sawmill.xml");
SawMill springSawMill = (SawMill)context.getBean("sawMill");
springSawMill.run();

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

(I know the Spring framework is more than that, but I'm thinking of the need for a DI container.)

In the first example it would also be trivial to change dependencies midstream:

// gotta chop it faster
saw = new ChainSaw();
sawMill.setSaw(saw);
sawMill.run();
+2  A: 

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

Mnementh
That has nothing to do with DI.
Paco
Ergo, the purpose of a configuration file is to defeat the type system.
Apocalisp
@Paco: funny you say that, because the very definition of DI (http://martinfowler.com/articles/injection.html) describes it as a way to "separate configuration from use", which is supposed to be useful precisely because you can change the implementation used at runtime without changing (and recompiling) the client code.
Rogerio
+1  A: 

It's important to understand that Spring is fundamentally two things, one built on 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 Spring code. Basically pick a Java technology and you'll probably find Spring has helper classes for it. This is so you can use ActiveMQ, Sun One MQ or whatever and abstract them being a Spring JmsTemplate and the same goes for data access technologies, Web services, etc.

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

cletus
+1  A: 

Have a look at my answer to this question, which explains at least parts of it.

krosenvold
+9  A: 

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

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

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

Ran Biron
This answer contradicts the "official" definition (http://martinfowler.com/articles/injection.html) of the Dependency Injection pattern, in my understanding.Class "A" being coupled to class "HandSaw" is a problem ONLY if the actual class implementing the "Saw" abstraction needs to be chosen at runtime. Otherwise, it's perfectly fine to directly instantiate a Saw implementation class in client code.DI is really about "separating configuration from use" in those situations where it's actually needed, not everywhere by default.
Rogerio
True only until you start testing. Then you wish you have some way to put "TestSawImpl", which just asserts, into A instead of a real "HandSaw", which require a stack of wood, a power source and a certified saw operator (and probably a medic standing by).
Ran Biron
No, it's always true. I can write unit tests easily by using a mocking tool. There is no need to ever create a `TestSawImpl` because the mocking tool allows me to mock any `Saw` implementation class with a single line of code, even if the concrete class is not known at compile time.
Rogerio
Introducing yet another layer of weaving / dynamic class loading / other tricks to make the tests work, taking them even further apart form the real world they're supposed to mock. It's true it can be done, but I'm all for the simple solution - have a setter and set it differently on your test.
Ran Biron
So, you don't use any mocking API at all for unit testing? A mocking tool as I described is not a bit more complicated than more "conventional" ones (the opposite is true, actually).
Rogerio
Actually, I prefer to supply my own mocks when possible - I try to resort to "magic" (weaving / class loader replacement) as little as possible. It tends to interfere with the product "magic".
Ran Biron
Fair enough, if you prefer mocking by hand, that's OK with me. Other people find the use of mocking APIs to be the simpler solution, though.
Rogerio
+3  A: 

I generally don't care about XML or Reflection based DI because, in my use cases, it adds unnecessary complexity. Instead, I usually go for some form of manual DI that, to me, seems 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 means I still centralize the coupling and have all the advantages of that, without the dependency on more complex DI framework or the XML configuration files.

Jasper Bekkers
This is a lot of repeating typing working in comparison with using an inversion of control container
Paco
You'll end up having to type nearly the same thing in the XML configuration files as you would in this case most of the time.
Jasper Bekkers
And in the case of Reflection based DI, this approach is way more transparent to anyone using the system.
Jasper Bekkers
reaction to point 1: That just means the DI framework you are looking at sucks. There are DI frameworks configured without xml.
Paco
reaction to point 2: The idea of DI is that users of the code don't have to bother what kind of implementation is used. If you care, don't use DI at that place. Thight coupling can be good to at the right place.
Paco
#1: Yes, unfortunately one of the constraints was that the language I was working with didn't have any kind of reflection support (C++) so nearly all of the alternatives for doing proper DI sucked.
Jasper Bekkers
#2: Not transparent in a way that the user actually knows what he's working with. Transparent as in "I can clearly see what this DI should do" and not "I need to read 50 pages of spring documentation". I didn't need the complexity, but I did need the freedom that that comes with DI.
Jasper Bekkers
+2  A: 

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

jfpoilpret
A: 

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

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

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

Peter Lawrey
+5  A: 

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

I think it makes more sense to put the "wiring up" in a configuration file rather than 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.
matt b
I thought the first 3 were perhaps less clear-cut than 4. :) External configs add indirection (complexity); maintaing plain old Java code can be more pleasant than maintaining code _and_ XML configurations (though modern IDEs have nice support for Spring); it comes back to the original question...
Jonik
Ergo, the essence of dependency injection is to defeat the type system?
Apocalisp
I'd argue that external files reduce complexity; instead of your wiring up happening all over the place the information is stored in a single location; but maybe that's just my opinion
matt b
+6  A: 

Spring has three features that are equally 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 an advantage to dependency injection when you compare that to a single call to new. In that case, the latter will certainly look simpler, because it's a single line of code. Spring's configuration will always increase the lines of code, so it's not a winning argument.

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

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

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

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

duffymo
+8  A: 

Dependency injection is a degenerate form of Implicit Parameters, and the purpose is essentially the same, to solve what's called The Configurations Problem:

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

Dependency Injection frameworks compensate for the lack of implicit parameters in the language. The benefit of implicit parameters is that you can declare that your program or function requires a run-time value of a specific type, or meeting certain constraints, and then a value meeting those constraints will be read from the environment which is implicitly passed to your program or function at run-time.

Apocalisp
+1  A: 

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

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

Jeff Olson
It's only a benefit IF there isn't another simple way to achieve the separation between clients and collaborators in unit tests. But the fact is that such a simple way does exist. There are mocking tools that do just that.
Rogerio
Indeed, you are right Rogerio...I've been recently using your *excellent* JMockit framework and it is really helping us write tests for our existing codebase that didn't use DI. Thank you!
Jeff Olson
A: 

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

mklhmnn
yep. I hate this as well - but all other indirect usage patterns (such as "command design pattern", auto-proxies...) also suffer from the same issue.
Ran Biron