2

I have a general understanding problem with dependency injection, independent of a specific dependency injection framework. Let's say I have a class which needs a runtime parameter:

class ClassWithRuntimeDependency {
  ClassWithRuntimeDependency (String myRuntimeParameter) {
    //...
  }
}

Now to get this runtime parameter into my class, documentations of several dependency injection frameworks tell me to use a factory. This factory takes the runtime parameter and creates an instance of ClassWithRunetimeDependency with it.

But let's say this ClassWithRuntimeDependency is kind of a very basic class which is needed by nearly all other classes: Class A -> Class B -> Class C -> Factory<ClassWithRuntimeDependency>.

Now I cannot create class C without this runtime dependency either, so I need to make a factory for it. But the same applies to classes A and B! This leads to a factory for class A with a runtime dependency which is only needed for constructing ClassWithRuntimeDependency. This means I do not inject a direct dependency into class A, which isn't best practice either (github). Instead of using factories everywhere, I know I could also introduce this runtime dependency to all needed methods, but this only shifts the problem.

Do I have a misunderstanding here or is there any better solution to this?


To further express the problem, this could be my classes A-C if I would have used factories everywhere:

// Needs a factory because of runtime parameter. 
// Imho, this is the only class which should really need a factory because 
// it is the only class having the  direct runtime dependency, all 
// others below are indirect
class ClassWithRuntimeDependency {
  ClassWithRuntimeDependency (String myRuntimeParameter) {
    //...
  }
}

// Needs a factory because of runtime parameter in ClassWithRuntimeDependendcy
class C {
  C(String myRuntimeParameter, @inject FactoryForClassWithRuntimeDependency reallyNeededFactory) {
    this.withRuntimeDependency = reallyNeededFactory(myRuntimeParameter);
  }
}

// Needs a factory because of runtime parameter in C -> ClassWithRuntimeDependendcy
class B {
  B(String myRuntimeParameter, @inject FactoryForC cFactory) {
    this.c = cFactory(myRuntimeParameter);
  }
}

// Needs a factory because of runtime parameter in B -> C -> ClassWithRuntimeDependendcy
class A {
  A(String myRuntimeParameter, @inject FactoryForB bFactory) {
    this.b = bFactory(myRuntimeParameter);
  }
}

(I did not use the syntax of a specific DI framework)

So I end up having an AFactory with a dependency that is only needed by ClassWithRuntimeDependency. Of course, I could also leave out the parameter in the constructor and use it methods only (as suggested here), but if I have in any of these classes many methods which needs this parameter, this really blows up my API in all dependent classes, therefore kind of only shifts the problem.

The only solution I came up so far is to inject a context object (or a provider of a context object), and fill this context object with runtime data. But this also leads to temporal coupling and makes it harder to test.

Toni
  • 1,593
  • 1
  • 11
  • 21
  • 1
    Instead of pushing your parameter into your classes, you might consider to autowire some kind of runtime context into your application. So each class is able to acquire the runtime parameters themselves. – Jan B. Jul 12 '17 at 08:43
  • Your basic assumption is wrong. Your application components should _not_ require runtime data during initialization, as explained [here](https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=99). – Steven Jul 12 '17 at 08:54
  • @Steven I know this blogpost. But this would mean adding this runtime parameter to a lot of methods in class A, B, C, doesn't it? In my opinion, this kind of shifts the problem, as stated above. – Toni Jul 12 '17 at 09:28
  • With a generic question, you get a generic answer :). Please update your questiob to make your example concrete. – Steven Jul 12 '17 at 09:32
  • 1
    @Toni, you don't go in much details, but have you had a look at Guice's [AssistedInject](https://github.com/google/guice/wiki/AssistedInject)? – Olivier Grégoire Jul 12 '17 at 10:00
  • @OlivierGrégoire Thanks for the hint; but as far as I understand it, this does not solve my question. If I use @AssistetInject in `ClassWithRuntimeDependency`, it creates a factory out of it (in background). Therefore I still have to create factories for all other classes A-C since they depend on a created instance of `ClassWithRuntimeDependency`, don't I? – Toni Jul 12 '17 at 10:33
  • @Matt I was already thinking this on my own, but I'm not sure if this is a good practice since it also makes my tests dependent of this context. – Toni Jul 12 '17 at 10:34
  • @Toni Okay, it's just a tad clearer. I believe that you're looking for what is called the "leg" problem: [it's solved here](https://github.com/google/guice/wiki/FrequentlyAskedQuestions) (look for "How do I build two similar but slightly different trees of objects?"), or if you want a generalized solution, you might find some help here: https://stackoverflow.com/q/6625837/180719 (it's a question I had a long time ago, but there are several hints and I posted the solution directly in the question as well). – Olivier Grégoire Jul 12 '17 at 10:37
  • 1
    @Toni, actually that's an advantage of that approach. You'd have to mock that context and Spring automatically injects it into your objects for your tests. Dependencies are not bad for testing, as long as you're able to easily mock them. – Jan B. Jul 12 '17 at 10:49
  • 1
    Do you think this might be a dupe of [this question](https://stackoverflow.com/q/32362711/1426891) or [this one](https://stackoverflow.com/q/7416974/1426891)? Personally I'm a fan of the child injector solution in the former, and think it would apply to your use-case well. – Jeff Bowman Jul 12 '17 at 21:24

1 Answers1

-3

This is why you should have Inversion of Control container and allow him to do dirty work for you. Like Spring does. You just point what kind of dependency you need for your bean through configuration and Spring inject this dependency.

rxn1d
  • 1,236
  • 6
  • 18
  • 6
    You don't explain how you can solve this problem with Spring. You state what the OP already knows (have a IoC container). Sorry, this is not a good answer. -1 until improvements happen. – Olivier Grégoire Jul 12 '17 at 10:02