5

Maybe i missed it in the documentation but i'm wondering how i should handle "helper Objects"?

code example:

public Path dijkstra(Node startNode, Node endNode) {
    Set<Node> nodesToInspect = new HashSet<Node>();  // should this Object be injected?
    Path path = new Path();  // and this one?

    while (!nodesToInspect.isEmpty()) {
        // some logic like:
        path.add(currentNode);

    }

    return path;
}

Should i inject everything or should i say at some point that the algorithm "knows" best what it needs? Should i try to eliminate every "new"? or are some object creations fine, for example API classes like HashSet, ArrayList, etc.

Absurd-Mind
  • 7,884
  • 5
  • 35
  • 47
  • http://stackoverflow.com/questions/1005473/must-dependency-injection-come-at-the-expense-of-encapsulation – sdasdadas Dec 19 '12 at 00:32

3 Answers3

2

Before you replace a simple new with dependency injection, you need to ask yourself "why am I doing this?" ... "what real benefit does it have?". If the answer is "I don't know" or "nothing", then you shouldn't.

In this case, I can see no real benefit in using DI in the first cases in your example code. There is no need for anything outside of that method to know about how the internal set is represented ... or even to know that it exists.

The other question you should ask is whether there is a simpler, more obvious way of achieving the goal. For example, the (most likely) purpose of using DI for the path variable is to allow the application to use a different Path class. But the simple way to do that is to pass a Path instance to the dijkstra method as an explicit parameter. You could even use overloading to make this more palatable; e.g.

public Path dijkstra(Node startNode, Node endNode) {
    return dijkstra(startNode, endNode, new Path());
}

public Path dijkstra(Node startNode, Node endNode, Path path) {
    ...
}

The final thing to consider is that DI (in Java) involves reflection at some level, and is inevitably more expensive than the classical approaches of using new or factory objects / methods. If you don't need the extra flexibility of DI, you shouldn't pay for it.


I just noticed that the two variables you are referring to are local variables. I'm not aware of any DI framework that allows you to inject local variables ...

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Google Guice would allow me to pass some Provider Object to my class, with which i could generate the Sets easily. Still i'm not sure if this is a good way. – Absurd-Mind Dec 19 '12 at 02:34
  • @Absurd-Mind - well passing a provider is not classic DI (a `Provider` is effectively a factory object). And besides, you need to ask yourself what (of value) are you actually achieving. – Stephen C Dec 19 '12 at 03:15
1

Remember the design principle: "Encapsulate what changes often" or "encapsulate what varies". As the engineer, you know best what is likely to change. You wouldn't want to hard-code the year 2012 into code that will live until next decade, but you also wouldn't want to make "Math.PI" a configuration setting either--that'd be extra overhead and configuration that you'd never need to touch.

That principle doesn't change with dependency injection.

Are you writing one algorithm and you know which implementation of Set you need, like in your Dijkstra example? Create your object yourself. But what if your co-worker is soon to deliver a fantastic new implementation of Set optimized for your use-case, or what if you are experimenting with different collection implementations for benchmarking? Maybe you'll want to inject a Provider until the dust settles. That's less likely for collections, but maybe it's more likely for similar disposable objects you might think of as "helper objects".

Suppose you're choosing between different types of CreditCardAuthService that may vary at runtime. Great case for injection. But what if you've signed a five-year contract and you know your code is going to be replaced long before then? Maybe hard-coding to one service makes more sense. But then you have to write some unit tests or integration tests and you really don't want to use a real credit card backend. Back to dependency injection.

Remember: code is malleable. Someday you may decide that you need to rip out your hard-coded HashSet and replace it with something else, which is fine. Or maybe you'll discover that you need your service to vary often enough that it should be Guice-controlled, and then you add a constructor parameter and a binding and call it a day. Don't worry about it too much. Just keep in mind that just because you have a hammer, not every problem is a Provider<WoodFasteningService>.

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
0

When working with DI, I prefer to avoid "new" whenever possible. Every instance you create outside the container (e.g. the guice injector) can not refactored to use injection (what if your "Path" instance should get a String "root" injected by configuration ... and you cannot use interceptors on those objects either. So unless it is a pure Entity-Pojo, I use Provides/Inject all the time. It's also part of the inversion of control (hollywood) pattern and testability ... what if you need to mock Path or Set in Junit? if you have them injected (in this case the Providers), you can easily switch concrete implementation afterwards.

Jan Galinski
  • 11,768
  • 8
  • 54
  • 77
  • good point, but this approach causes "constructor pollution". Already in this simple case my constructor would need two Providers and i didn't even post the full algorithm or dependencies to other classes. Furthermore how should a user determine which Set is the correct/best one for an algorithm? – Absurd-Mind Dec 19 '12 at 16:19
  • If you use constructor injection, the instance will also be created by guice, so the User doesnt decide which implementation to use. But in Cases like this, i prefer field injection anyway. – Jan Galinski Dec 19 '12 at 16:52
  • Field injection forces me to use dependency injection while testing, which i don't want and/or can't use. Yes, the instance is created by the provider, but the user still has to choose (or has the possibility to change) the correct/best provider. – Absurd-Mind Dec 19 '12 at 16:58