The One Correct Way to do Dependency Injection
A couple of weeks ago a coworker told me that they have a little utility in their projects in order to set private fields for tests. He kind of claimed they needed that since they are using Spring, which in production sets the dependency. I was a little confused. But after looking into it I realized that there is an (anti)pattern going on that needs some fighting against. Lets have a look at the pattern.
Spring (and other DI frameworks) can inject dependencies into private fields. It looks like people like to use this because it combines some compelling properties:
- Very little boilerplate code. All you need is a simple annotation on the field.
- Since the field is private nobody outside the class can change it.
But now when you want to test your class and want to assign e.g. a mock to the field you have to either setup a Spring context in your tests or use reflection in order to access the field (just as Spring does).
Now something is really starting to smell bad here. One of the purposes of Dependency Injection is to decouple code, which in turn should make the code easier to test. Now that didn’t work out to well.
So how can we clean up this mess? We can write a setter for the field. That solves the testing problem. It adds a little boiler plate code, but hey we are talking Java here, so you should be used to that. But now we have a setter which anybody can call at any time. At the very best that doesn’t make any sense at all in the production environment. In the worst case somebody actually uses it and creates some ugly bug.
So what do you do when you don’t want anybody to change a field? Correct you make it final. Great, now you have a compile time error in the setter.You can only set a final field in a constructor. This leads naturally to the solution of all our problems (and possibly to world peace): Make your dependency a constructor argument!
This is the only place where dependencies really belong. If you try to stick to that rule I’d expect you’ll encounter the following two problems.
- You’ll find classes with lots and lots of constructor arguments. I’ll bet that class does a lot of different things and really needs to get broken down into smaller pieces.
- You’ll find cases where a class A needs an instance of B and B needs an instance of A. This is a typical case of a circular dependency and is obviously bad. In my experience the solution is either to make B a part of A when the two are so strongly dependent that they really should be one class. More often though there is at least one more class C hiding in there so that B doesn’t need A but only C.
So really the problems you’ll find when doing constructor based dependency injection are problems that are present in your current code already. So don’t shoot the messenger, but fix the problems in your code and go for constructor arguments for dependency injection.
From http://blog.schauderhaft.de/2012/01/01/the-one-correct-way-to-do-dependency-injection/
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)





Comments
Tomasz Nurkiewicz replied on Tue, 2012/01/10 - 3:26am
The pragmatic problem with constructor injection is that it requires you to declare dependencies three times: in the constructor argument list, as a field and in the assignment from the former to the latter. This is really ugly and not very DRY. Fortunately in Scala constructor arguments used outside of the constructor are automatically changed to final fields:
class Foo @Autowired() ( bar: Bar, jdbcOperations: JdbcOperations) { def serverTime() = bar.format(jdbcOperations.queryForObject("SELECT now()", classOf[Date])) }See my article for details.Andrew Spencer replied on Tue, 2012/01/10 - 4:17am
in response to: nurkiewicz
Tomasz, I agree, and there is another problem with constructors, which is that parameters aren't named (as they are with setter injection). You can get round this by using a builder but then you have even more repetition (so much so that I've never yet found it worthwhile to write one).
BTW I think that shorthand for combining constructor arguments + final fields is Scala's nicest feature when used purely as an improved Java (that is, aside from the features like FP that are totally alien to Java).
Jens, nice article and I will be bookmarking it for use in future
argumentsdebate.Michal Xorty replied on Tue, 2012/01/10 - 4:47am
Perosnally I really fancy having @Inject or @Autowired directly at field (even thought I don't use it often since I also perfer constructor injection for mentioned reasons), but I'd like to have something like this:
@Inject(style = Injecting.CONSTRUCTOR) private SomeService service;Annotation is therefore moved from constructor to field which makes code more readable for me.
Mladen Girazovski replied on Tue, 2012/01/10 - 5:00am
Just as Jens said,
the "Problems" (if you want to call it that way) with constructor injection are not DI problems.
@Michal
The issues with field injection mentioned in the article (and in every beginners guide for DI also) is that it isn't as easily testable as it should be, just try to replace your service with a mock for a test...
Michal Xorty replied on Tue, 2012/01/10 - 5:05am
Mladen Girazovski replied on Tue, 2012/01/10 - 5:10am
Rogerio Liesenfeld replied on Tue, 2012/01/10 - 8:15am
Michal Xorty replied on Tue, 2012/01/10 - 8:33am
That's usually not refactoring friendly (since you must specify private field name as String) and you should test public API not internals. It might not do unit testing harder, but it makes your object design less valid.
Mladen Girazovski replied on Tue, 2012/01/10 - 10:05am
@Rogerio
True, many "modern" Mockingframeworks can do amazing (or rather scary?) stuff and make previously un-testable code testable (like PowerMock, intercepting the new Operator, etc.).
Those "tricks" don't necessary make the test code simplier, easier to understand, maintain oder faster.
I prefer Constructor and even Setter Injection instead of tricky mocking libs.
Howard Lewis Ship replied on Tue, 2012/01/10 - 11:17am
In addition, Tapestry can handle the mutual dependency situation: since Tapestry proxies services (for lazy initialization, and other reasons), each service implementation will be injected with a proxy to the other. As long as the proxies' methods are not invoked from the constructor, all will work (otherwise, Tapestry will produce a detailed report pinpointing the problem).
I prefer constructor injection; however, in Tapestry components, injection has been through fields. It gets a bit murky; in Tapestry component classes, a bytecode transformation step means that injection can take place into fields without using reflection. Field injection into services, which are not bytecode transformed, came later and does use reflection.
More details on the Tapestry home page. Tapestry IoC is designed to be used independently from the Tapestry web framework.
John J. Franey replied on Tue, 2012/01/10 - 9:34pm
I agree with the former, except in the case where setting the property does make sense. Maybe the injected object is a default that is suitable for most but not all cases.
I also agree with the latter. However, the risk would be reduced by using default access level on the setter. Still a bit messy, but I think I prefer it to constructor injection.
Michal Gruca replied on Wed, 2012/01/11 - 3:43am
It should be simple. If the object needs some services (or beans as spring call them), then you should pass it in the constructor.
If the dependency is needed for one of the methods, then pass it to that method directly. If there is a group of methods that use one set of dependencies and other group that use other set of dependencies then you got 2 objects, that want to come out. Don't stop them and refactor them out.
If you stick to that simple rule it's easy to test as you always know which fields should be initialized. It's refactoring friendly and you can mark fields as final (what sometimes is useful, when writing inner class
Michal Gruca replied on Wed, 2012/01/11 - 3:46am
Fabrizio Giudici replied on Wed, 2012/01/11 - 2:19pm
Anyway I prefer injection by constructors too because it enables final fields, thus possibly immutable objects. BTW, with Lombok you can just annotate with @RequiredArgsConstructor the class and the compiler will generate automatically a constructor for all the private final fields that are not initialized. Thus, the thing is perfectly DRY.
The point is that sometimes you just can't use constructors because of a framework that manages the object life cycle and doesn't support custom constructors. JEE seems to me the most evident, but even Android is another good example. Thus, I don't think we can avoid @Inject and stuff that does injection by reflection and private fields.
Carla Brian replied on Mon, 2012/05/14 - 8:44am