3 ms·
If you don't like frameworks, just inject dependencies manually. It's very simple, it scales to hundreds of classes and then you can migrate to DI (but probably
by vbezhenar 5y ago
If you don't like frameworks, just inject dependencies manually. It's very simple, it scales to hundreds of classes and then you can migrate to DI (but probably you won't reach that line ever, microservices are here to stay).
class Service1 { ... }
class Service2 {
private Service2 service2;
void setService2(...)
void afterPropertiesSet() { requireNonNull(service2, "service2"); }
}
main() {
Service1 service1 = new Service1();
Service1 service2 = new Service2();
...
service2.setService1(service1);
...
service1.afterPropertiesSet();
service2.afterPropertiesSet();
...
I used setter injection and afterPropertiesSet callback. It allows not to worry about construction order and loops. Also it allows to fail fast if dependency not set. And it's compatible with Spring, if you ever decide to use it. But using constructor injection is even simpler and for applications with few dozens of services that might be a more clean approach.
- gowld 5y agoHuh? Microservices are why you'd have hundreds of classes and lots of dependencies to coordinate.
- jmull 5y agoService2 has a setService2 method and private Service2 member? Maybe just a typo, but doesn't exactly allay my concerns that this is another spaghetti-code pattern.
- vbezhenar 5y agoYep, it was typo, sorry. Though recursive dependencies sometimes are necessary (usually to work around some AOP quirks).
- Someone1234 5y agoThat example code looks like the worst of all worlds. First off you aren't using interfaces, which makes it unclear how you're writing test versions of your classes, but also you're setting up a massive foot gun by having a dependancy-injector with completely uncoupled consumers later in that same class. For example if I have multiple setService (e.g. serviceA, serviceB, and serviceC) and I want to call .afterPropertiesSet(), how do I know which it depends on externally? Do I have to go inspect afterPropertiesSet()'s code? Or just wait for an exception to be thrown in a Run<->Throw<->Fix endless cycle? Eww. There's a very, very, good reason why all DI frameworks have you use interfaces and the class's constructor to inject. Because if injection happens during class construction, every call after can make the assumption those things exist. By decoupling that assumption from those calls, you've created a development hell (i.e. this is an unmaintainable mess). There're two good ways to do DI: - During class construction. They then always exist in the class's scope. - Every method call individually takes in all explicit dependencies (e.g. GetMagic(iDatabase database, iDateTime dateTime, ...)). Your example is neither of those.
- vbezhenar 5y ago> First off you aren't using interfaces, which makes it unclear how you're writing test versions of your classes Use interfaces if you want, there are different approaches to testing. Some classes are fine to inject as they are. Some classes are fine to mock with libraries like Mockito. And sometimes interfaces makes sense. > For example if I have multiple setService (e.g. serviceA, serviceB, and serviceC) and I want to call .afterPropertiesSet(), how do I know which it depends on externally? I'm not sure I follow you. You just wrote that class, surely you know what dependencies you need. > There's a very, very, good reason why all DI frameworks have you use interfaces and the class's constructor to inject. You're wrong here. I don't know a single DI framework that forces you to use interfaces and constructors. Setter injection or field injection is supported everywhere, at least in Java world. > By decoupling that assumption from those calls, you've created a development hell (i.e. this is an unmaintainable mess). I used that approach. It works good and it scales without issues with quite complex dependency chains. One issue is a little bit of mundane work, but it's not a lot and simplicity worth it. > There're two good ways to do DI: > - During class construction. They then always exist in the class's scope. Constructor injection is terrible approach to use without DI framework. You'll need to move your initialization code around almost every time your dependencies change. You inevitably will invent yet another DI framework, buggy and incomplete and at this point you lost. Also it does not allow for circular dependencies, which sometimes are necessary. Another point is that Java does not have named parameters and long constructor calls are hard to read (recent Idea improves that a little bit with inlay hints, though). > - Every method call individually takes in all explicit dependencies (e.g. GetMagic(iDatabase database, iDateTime dateTime, ...)). That's not a dependency injection, as it requires to pass dependencies through every call chain. If you suddenly need some service deep inside your chain call, you have to change all functions which lead to that call. It might be a suitable approach for some 1000-LoC one-file program, but it does not scale.