27

I (think I) understand the purpose of dependency injection, but I'm just not getting why I need something like Guice to do it (well, obviously I don't need Guice, but I mean why it would be beneficial to use it). Let's say I have existing (non-Guice) code that's something like this:

public SomeBarFooerImplementation(Foo foo, Bar bar) {
    this.foo = foo;
    this.bar = bar;
}

public void fooThatBar() {
    foo.fooify(bar);
}

And somewhere higher level, maybe in my main(), I have:

public static void main(String[] args) {
    Foo foo = new SomeFooImplementation();
    Bar bar = new SomeBarImplementation();
    BarFooer barFooer = new SomeBarFooerImplementation(foo, bar);

    barFooer.fooThatBar();
}

Now I've basically got the benefits of dependency injection, right? Easier testability and so forth? Of course if you want you could easily change the main() to get implementation classnames from configuration instead of hardcoding, too.

As I understand it, to do the same in Guice, I'd do something like:

public SomeModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(Foo.class).to(SomeFooImplementation.class);
        bind(Bar.class).to(SomeBarImplementation.class);
        bind(BarFooer.class).to(SomeBarFooerImplementation.class);
    }
}

@Inject
public SomeBarFooerImplementation(Foo foo, Bar, bar) {
    this.foo = foo;
    this.bar = bar;
}

public static void main(String[] args) {
    Injector injector = Guice.createInjector(new SomeModule());
    Foo foo = injector.getInstance(Foo.class);

    barFooer.fooThatBar();
}

Is that it? It seems to me like just syntactic sugar, and not particularly helpful syntactic sugar. And if there's some advantage to breaking the "new XxxImplementation()" stuff out into a separate module rather than doing it directly in main(), that's easily done without Guice anyway.

So I get the feeling that I'm missing something very basic. Could you please explain to me how the Guice way is advantageous?

Thanks in advance.

assylias
  • 321,522
  • 82
  • 660
  • 783
user1759936
  • 273
  • 2
  • 5
  • 3
    I think you _should_ use manual DI until it becomes painful to do so. And when you start manually composing deep object graphs (for example, Foo has a constructor argument which has a constructor argument which has...), then the benefit of an autowiring DI container becomes very clear. – default.kramer Oct 19 '12 at 16:40

2 Answers2

20

In real life and bigger applications the level of components is not just 2 (as in your example). It may be 10 or more. Also requirements and dependencies can change at a whim.

When doing "DI by hand" this means, that you must change all paths to all components and their transitive dependencies by hand. That might just be quite some work.

With "real DI" (i.e. Guice, Spring, CDI) you just change one or two affected components and the wiring (the Module on Guice) and that's it.

Also: Imagine that some of your deeply nested components are in factories which should be called by other components. In Guice you just inject a Provider<Foo> and that's it. When doing it by hand you must drag the dependencies of the factories and their instantiations throughout your code.

That's even more frustrating.

Edit To clarify the last point: Image you have some deeply nested logic (10 or more levels). At the bottom your logic shall create a factory based on some value e.g. the actual temperature: if "warm" then a Cooler is wanted, if "cold" then a Heater. Both have the interface TemperatureAdjustor. Both Cooler and Heater need a lot of different dependencies to get their job done.

Since the instance type of the factor you cannot create one in main and hand it down to where it is needed. So you end up handing the dependencies of both factories down to the location where they are needed. ( "a lot dependencies" plus "a lot of dependencies" is "too many to stay sane")

With "real DI" like Guice you can use AssistedInject to avoid the clutter of dependencies and give only the actual business value (here: temperature) to the factory and be done. The wiring of the factory is done in the module.

A.H.
  • 63,967
  • 15
  • 92
  • 126
2

Handcoded DI is real DI.

When you consider the complexity of an IOC container then you have to look deeper to justify why you would use it. I was anti-IOC for a long time, being able to design and implement clean, fast, neat hand-coded, real DI. But I relented to the idea of containers because they help standardize implementation and provide a more config-based mechanism for adding new decisions to the dependency equation. And someone else already wrote them for you so you don't have to spend time hand-coding.

Many say to use containers in big apps and hand code in little ones. I say the opposite. Little apps are not so performance constrained. Go for the implementation speed of using a packaged container. In big apps where you will spend a lot of time optimizing, consider hand coding your DI.

edit: but let me add that, IOC Container or hand-coded, always use DI and code to an interface - no matter how simple the apps are that you're working on. Write great code even in little apps.

dale
  • 21
  • 1