Preslav's Thoughts and Ramblings

Write Better Software: Avoid Setter Injection at All Costs

15 May 2013 | tip, dependency injection, software design

A part of my: "Tips for writing better software" series

IMHO, setter injection (SI) is one of the things that keep reoccurring in production code, mainly for legacy reasons. Older versions of some dependency injection (DI) frameworks required it because of the way they instantiated classes - using reflection.

What you should be aiming at, is relying on constructor injection. This guarantees that there is one and only place in the entire class, where injection occurs, and where potential checks must be made. With setter injection, even if you rely on getter methods to do the verification, such checks will not be executed until the getter is actually invoked. So, not only are you obliged to never forget using the getter (even inside the class itself), but it might turn out to be too late. Constructor injection guarantees the integrity of the injected dependencies before the object has even having been instantiated.

Not only that, but constructor injection helps make the class immutable. With setter injection, anyone, at any point in time, having access to an instance of your class, can set a new value for any of the expected dependencies (even set it to null). This is certain to cause you a few sleepless nights, chasing the culprit down the line.

What about putting those 10 dependencies in a constructor?

With all the nice stuff being said about CI, it comes with a greater design and refactoring cost. Unlike setter injection, where you can literally slap dozens of dependencies, having all those as constructor parameters not only sounds silly, but it a sign of a bad design. Well how do you then solve that? By thinking.

One of the important concepts that developers need to learn early on is that of the layers of abstraction. The other is the single responsibility principle. Both of them use a lot of words that simply mean: “reduce what a component does, and provide barriers to how much it knows about its surroundings. In design pattern terms, a barrier usually means a Facade. The purpose of facades is to serve as layer of abstraction, by hiding the complexity of the implementation, behind a single interface.

In simple terms, when you are examining the dependencies that a given class relies on, try to bundle them in logical coherent groups. The trick is to end up with a maximum of 3 (based on experience) such groups, where each group contains a maximum of three of the dependencies.

Further Reading:

Refactoring to Aggregate Services: http://blog.ploeh.dk/2010/02/02/RefactoringtoAggregateServices/
How to avoid Dependency Injection constructor madness?: http://stackoverflow.com/questions/2420193/how-to-avoid-dependency-injection-constructor-madness
Constructor Injection vs. Setter Injection | Miško Hevery: http://misko.hevery.com/2009/02/19/constructor-injection-vs-setter-injection/