I only miss DI. I miss being able to say "this system depends on these external things" and having a consistent, convenient way of sharing/swapping/testing those components and dependencies.
The solution in other languages? Unstructured globals, deep argument passing, or monkey patching with mocks?!
Yea, I can write simpler code without DI... By ignoring a bunch of stuff.
If you write classes with final fields, with a constrcutor that takes the class' dependencies,and don't use static fields to hold mutable data.
You are doing DI. Just call `new` yourself, instead of having the framework do it for you.
This reminds me of SQL/ORM debate. "Just use SQL!" Sure, until you get tired of typing the same SQL over and over and realize you can cut out most of that crap by adding an ORM.
https://steve-yegge.blogspot.com/2006/03/execution-in-kingdo...
And adding an ORM isn't either/or. You can still use native SQL when necessary.
But at that point, why would I want to?
There are reasons I wouldn't want to, but there is no inherent value, to me, in manually calling new.
In the main method, then you can pass the configured values wherever you need to when new-ing classes.
> At some point you want a user-facing UI where the available features (which are generally classes) are listed and the user can choose the feature, say which log backend is enabled, without having to change code - that's the whole point of it. (And the most tedious code to write by hand - a complete waste of time)
I consider DI a valuable pattern, but I've never experienced anything close to this need.
But frankly, how will you call that new if it depends on a class which is a singleton, another which has some more complicated scope so it may or may not have to be reused? DI is not only about calling new..
Spring takes care of that, but doing it manually (and without dynamic proxies) would add to the verbosity.
You just have a class/classes that construct/wire all of your singleton objects and passes the required dependencies into their respective constructors as necessary.
Here is a contrived example of what the wiring code might look like for a web app that uses a database.
public static void main(String[] args) {
MyConfig config = readConfigFile();
DatabaseConnection dbConn = new DatabaseConnection(config.dbHost(), config.dbPort());
UserDao userDao = new UserDao(dbConn);
UserController userController = new UserController(userDao);
List<Controller> controllers = List.of(userController);
WebServer webServer = new WebServer(config.listenPort(), controllers);
webServer.runAndBlock();
}And I fail to see why it's a problem. If your FooService depends on a BarService, which depends on a BazService, and BazService needs a database connection, then that means your FooService really does also depend on a database connection. Hiding that information, to me, seems like a mistake. Can you articulate why one would prefer not to have FooService explicitly require that database connection, or am I inadvertently arguing against a straw man? If so, please correct me, because I'm asking sincerely.
Of all the time I spend thinking about my code and writing code, I truly can't say that adding a dependency and having the compiler complain until I fix a bunch of constructors has really caused me that much grief. And I'm not going to pretend that it has never been the case that I've had to fix 20 constructors.
I would prefer not to have to fix 20 constructors.
It's tedious and time consuming. The intermediate classes that _do technically depend on FooService because BarService does_ - the intermediate classes don't care! It clutters the code everywhere else for minimal benefit.
Manually, you see all your dependencies just shy of main where the binary initializes them all and starts passing things down. In DI, you have a module file somewhere with them all.
But thank you for responding anyway.
(As a clarification, in case it's needed: I obviously didn't LOVE it when I had to update 20 ctors after changing a somewhat fundamental "service" to need a new dep. My point was that, even as painful as that was, it wasn't that bad and it's usually much less bad than that.)
I guess the (philosophical) difference comes to this statement:
> The intermediate classes that _do technically depend on FooService because BarService does_ - the intermediate classes don't care!
I can definitely understand what you're saying there, but it's interesting to me that I don't see it that way. I think I'm just less pragmatic and more... "academic" (?) about how I read and understand my own code. If X depends on Y and Y depends on Z, I'm comfortable with X explicitly depending on Z because I imagine "inlining" Y's functionality in X. Either that or you turn Y into an interface and then X only depends on IY. But, my brain just likes the explicit continuity I guess.
Cheers!