3 ms·
Dependency injection is a good pattern from a testability POV. Trying to test something that instantiates its own dependencies is frigging hard. But with DI, a
by EdwardDiego 6y ago
Dependency injection is a good pattern from a testability POV. Trying to test something that instantiates its own dependencies is frigging hard.
But with DI, a service needs to talk to the database? I can either spin up an embedded DB, or, I can pass it a mock DAO to verify it is using the data layer as expected.
(And of course, if there's a real DAO, I'm most likely going to write an integration test that does involve an embedded DB, just to be sure that our Postgres flavoured SQL works. And surprisingly, spinning up an embedded PG isn't that slow. And certainly better than trying to convince HSQL that it's Postgres.)
That said, something I've spent years trying to beat into my fellow Java developers is that the best form of dependency injection is... constructor args.
Make your dependencies constructor args, then you can "inject" whatever the hell you like that conforms to the expected type. You can stick @Autowired on the constructor if you like, go nuts. But even without, well hell, you might not actually need that DI framework, and it's still in a testable space.
As soon as someone starts sticking @Autowired on class fields though, you're fucked. You're annotating JUnit tests with Spring crap, or using Mockito combined with some reflection libraries... ...it's all downhill from there.
But! None of that cruft means that the DI pattern is bad. It's just that people have done very bad things in it's name.
- knuthsat 6y ago> Make your dependencies constructor args, then you can "inject" whatever the hell you like that conforms to the expected type. You can stick @Autowired on the constructor if you like, go nuts. But even without, well hell, you might not actually need that DI framework, and it's still in a testable space. Yes, this is exactly what I mean, the amount of wrong stuff that is in the constructor makes me believe DI is a solution for something else. Infinite amount of times have I encountered stuff in the constructor that shouldn't be there. Object A has a constructor with a bunch of dependencies, one of them is Object B. The proper way is for Object B to use object A instead. Blindly injecting everywhere is my problem with DI. Without these frameworks one would see that dependencies flow in a different direction if proper interfaces are defined. For example, React Context and useEffect hook is implicit DI yet people have no idea it's DI and I see codebases using React + DI framework instead. The only DI magic is the topological sort of dependencies and automatic construction. That stuff is 20 lines of additional code so that React.Context and useEffect work the same way.
- marcosdumay 6y ago> But! None of that cruft means that the DI pattern is bad. It's just that people have done very bad things in it's name. DI adds complexity to the code just by itself. The best ways to implement it are the ones that add as little complexity as possible, but they are still adding complexity. So, I wouldn't call it a "bad pattern", but it's not something that should be carelessly decided. The best DI is still no DI, you go for the second best only when you can't use the best.