Hacker News new | ask | show | jobs
by trbvm2 3926 days ago
Dependency Injection (frameworks) -1

Use constructors. This makes perfectly testable classes and is vastly simpler than encouraging more XML as code. Any proper dependency injection framework should work quite well with regular constructors.

4 comments

Your comment is unclear. Are you encouraging people to NOT use DI frameworks, or using them only with constructor-based injection?
The latter. DI frameworks are a convenience item, they should probably not have a big effect on the way the code is written. You get most of the benefits of DI just from not new'ing up things within your classes.

The main advantage of keeping the constructors around is that you retain more flexibility in interacting with that bit of code.

I think people use DI just because Java constructor syntax is so incredibly verbose:

    public class MyClass {
      @Inject private String foo;
    }
versus

    public class MyClass {
      private String foo;

      @Inject public MyClass(String foo) {
        this.foo = foo;
      }
    }
Constructors have their problems. First: constructor calling is not readable because Java misses named parameters. It's fine when there are 2-3 dependencies, but not more. Second: circular dependency is not possible. So I prefer to use setter dependencies, as they don't have those problems.
Constructor calling is readable either with an IDE, or using distinctive types. If the constructor is unclear, it's a smell.

I'd also say that making circular dependencies not possible is a feature, not a bug. They go against everything we've learned about computing in the last 20 years. Entire languages do not have variables, or at least heavily discourage them, and the gains outweigh the costs.

Setter dependencies have other major disadvantages, like how easy they make creating partially initialized objects: Refactor something in one place, adding a parameter, and good luck making sure that the change is made everywhere else. A constructor guarantees fully initialized objects, and that's a great things.

Java pays for the mistakes of the early 2000s: The Java Bean pattern, and all the infrastructure around it, makes Java code be full of default constructors that force us into mutable objects and nullable fields.

If there is a defense for your argument for setter dependencies, is that we cannot count on any of the nice things that come with, say a Scala case class, because so much Java code out there is built with standards from a less enlightened era that using what would be better standards in any more modern language leads to inconsistencies, due to all the ancient code using Spring, Hibernate, Guava and EJB code that is hanging out there, and that would lead to inconsistencies for those trying to make Java actually use some of its new functional programming inspired features.

> First: constructor calling is not readable because Java misses named parameters. It's fine when there are 2-3 dependencies, but not more.

If you have more than 2-3 dependencies, it might be time to refactor things, as your class may be doing too many things.

> Second: circular dependency is not possible.

Circular dependencies are possible with constructor injection when using a DI framework (e.g., using a Provider in Guice [1], or the equivalent in Dagger).

[1] https://github.com/google/guice/wiki/CyclicDependencies

I agree on the first. I'm only advocating for keeping the constructor as the DI entry point. A DI framework can then be used for the convenience they provide in wiring things up at main.

Regarding the second: I think circular dependencies should be avoided whenever possible so I view the difficulty constructors create there as advantage rather than a problem.

https://en.wikipedia.org/wiki/Circular_dependency#Problems_o...

By using a DI framework to wire up constructors you tend to use the compile-time checking that your app is wired correctly. Which is unfortunate. You only find out when you try to run it.

Perhaps things being too big to wire together manually is a smell that the application is getting rather large.

There are also other patterns that can be used to make manual dependency injection less of a pain. I wrote about some of them here

http://benjiweber.co.uk/blog/2014/09/13/frameworkless-depend...

I'd rather inject some lazy somethingsomething-provider reference wrapper to break the occasional circular dependency than work with a bunch of nonfinal references that supposedly never change, because DI, but are otherwise indistinguishable from true working state.
Dependency injection is brilliant for testing. Prior to DI you often has to have two constructors, one for prod, one for testing. DI took that away.