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.