The Dependency injection pattern is just not that great in general. There are alternative patterns that are better.
It is not a problem with frameworks. Think about it. If the pattern was good, then a good framework must exist. If no good framework exists then logically it is very likely that Something is wrong with the Pattern itself.
Anyway the reason why DI is bad is because it's too complex. In your program, you should have logic, and then have data move through that logic to produce new data or to mutate.
When you have dependency injection, not only do you have data moving through logic, but you have logic moving through logic. You are failing to modularize data and logic and effectively creating a hybrid monster of both data and logic moving through your program like a virus.
The pattern that replaces dependency injection in this: functions. Simple.
Have functions take in data and output data then feed that data into other functions. Compose your functions into pipelines that move data from IO input to IO output. If you want to change the logic you simply replace the relevant function in the pipeline. That's it.
One very typical pattern is to have IO modules get injected into modules so that one can replace these things with Mock IO during unit testing. With function pipelines things like IO modules should be IO functions, not modules injected into other modules. When you want to unit test your function pipeline without IO simply replace the IO functions with other mock IO functions. That's it. I will illustrate with psuedo code below.
compose(a,b) = lambda x : a(b(x))
a * b = compose(a,b)
pipeline = IOoutput * x * y * z * f * IOinput
pipeline()
The above is better then:
class F(IOinputClass):
f(x) = IOinput()
class Z(F)
z(x) = F.f(x)
class Y(Z)
y(x) = Z.z(x)
class X(Y)
x(x) = Y.y(x)
class IOOutput(X)
print(x) = print(X.x(x))
pipeline = X(Y(Z(F(input))))
pipeline.print()
You can see the second example is more wordy and involves unnecessary usage of state when you inject logic into the module. (I left out the constructor that assigns the class instance to state but the implication is there).
Dependency injection is a step backwards. It decreases modularity by unionizing state with logic. It's a pattern that became popular due to the prevalence of using classes excessively. If you can I would avoid this pattern all together.
No. I know what DI is. The debate is similar to imperative vs. OOP. But this is not exactly what I am referring to. Literally look at the second example, it's DI over and over and over again.
I am referring to DI 100%. Function composition works better and is a replacement for DI. You're the one that isn't getting it.
Either way, imperative programming and OOP are orthogonal concepts. There never really was an argument about imperative vs. OOP.
Additionally function composition is an FP concept. I'm not promoting FP over OOP here... far from it, I am simply saying that specifically for DI, you can borrow a pattern from FP and use it in place of DI because function composition is a much better pattern.
It is not a problem with frameworks. Think about it. If the pattern was good, then a good framework must exist. If no good framework exists then logically it is very likely that Something is wrong with the Pattern itself.
Anyway the reason why DI is bad is because it's too complex. In your program, you should have logic, and then have data move through that logic to produce new data or to mutate.
When you have dependency injection, not only do you have data moving through logic, but you have logic moving through logic. You are failing to modularize data and logic and effectively creating a hybrid monster of both data and logic moving through your program like a virus.
The pattern that replaces dependency injection in this: functions. Simple.
Have functions take in data and output data then feed that data into other functions. Compose your functions into pipelines that move data from IO input to IO output. If you want to change the logic you simply replace the relevant function in the pipeline. That's it.
One very typical pattern is to have IO modules get injected into modules so that one can replace these things with Mock IO during unit testing. With function pipelines things like IO modules should be IO functions, not modules injected into other modules. When you want to unit test your function pipeline without IO simply replace the IO functions with other mock IO functions. That's it. I will illustrate with psuedo code below.
The above is better then: You can see the second example is more wordy and involves unnecessary usage of state when you inject logic into the module. (I left out the constructor that assigns the class instance to state but the implication is there).Dependency injection is a step backwards. It decreases modularity by unionizing state with logic. It's a pattern that became popular due to the prevalence of using classes excessively. If you can I would avoid this pattern all together.