Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should i inject Objects needed for execution of an algorithm? Should i inject everything?

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.

like image 533
Absurd-Mind Avatar asked Oct 22 '22 21:10

Absurd-Mind


2 Answers

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

like image 135
Stephen C Avatar answered Oct 27 '22 09:10

Stephen C


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

like image 33
Jeff Bowman Avatar answered Oct 27 '22 11:10

Jeff Bowman