I have recently been involved in a debate about the quality differences between constructor and setter dependency injection. In my opinion constructor dependency injection has multiple advantages over setter injection. Here are some reasons why.
Reasons why constructor injection is to be preferred
Objects must enforce their state
We must try as much as possible to keep objects from being in an inconsistent state. By using constructor injection you make sure that an object cannot be created without passing in it’s required dependencies. If you use setter injection, until all mandatory dependencies are set, the object is in an inconsistent state.
You could argue that you can do validations at runtime to see that all the dependency of the class are met. But this can easily be forgot and a source for bugs. You are basically redoing what the compiler is meant to do by adding extra complexity. Plus, if you forget something in a constructor injected object, most modern IDE’s will spot that problem without even needing to run the code.
Getters and setters are a code smell
Getters and setters should be avoided because they break encapsulation, a fundamental principle of Object Oriented Programming. They allow too much access inside the inner workings of a class and expose it’s inner state to the outside.
They also break the Tell Don’t Ask principle. You should tell an object what to do with it’s internal state, not take it’s state out, modify it, and then pass it back in.
Setter injection creates temporal coupling
By using setter injection, you also create temporal coupling between the methods in your object. You must ensure that the dependency setters are always called before the methods that use those dependencies. This is not something that can be completely avoided, but it should be kept to a minimum; the nature of the language makes sure that the constructor is always called first.
The depedencies of an object are more visible in the constructor
This might be an opinion and a matter of preference,but I find it easier to understand the collaborating objects by looking in the constructor instead of scanning the whole class for getters and setters, and even more, determine if they are optional or required.
Arguments against constructor injection
Too many parameters in the constructor
I agree that you should limit as much as possible the parameters of any method, the constructor being no exception.
But the truth is, that removing the parameters from the constructor and moving them to setters, only sweeps the problem under the rug. Your class still has those dependencies, but they are now hidden and much harder to spot.
The solution to this is to think better about the design. When a class has too many dependencies, it’s a strong indication that this class does too much and it’s in violation of the Single Responsibility Principle. You might need to break it down into smaller classes. Or you are missing an abstraction in it’s dependencies.
I agree that when it comes to optional dependencies, setter dependency injection might work better. You could inject this kind of dependencies using setters and check for them not being null at runtime.
An alternative to using setter injection in the case of optional dependencies is the use of the Null Object Pattern. So you make an optional dependency required, but in case it’s not needed you send a Null Object with the same interface. This also reduces the need for null checks, which are just added complexity.
If a class has optional dependencies it might be a sign that the class is breaking the Single Responsibility Principle and it could be broken into smaller classes each with it’s own required dependencies.
When injecting dependencies in the constructor you could get cases where class A depends on class B, and class B depends on class A (just a simple example, these could get more complex). In this case you won’t be able to instantiate any of the classes. This again is a sign of bad design. You might need to merge A and B in a single class. Or, more often, there is a class C hiding in there, where functionality from both A and B belongs.
As you can see there are a lot of advantages of using constructor injection. The main advantage of constructor injection is that it makes the code more maintainable and easier to read. It also gives you great insights into the design of you system, by raising big warning flags about potential design problems.