Constructor injection is theoretically superior:
Before I built RobotLegs I was sold on constructor injection. My prototype, however, used SmartyPants-IOC which lacked constructor injection, so I bit my lip and used setter injection. In practice I found that often, especially with framework actors, it was incredibly convenient.
Unless you use a code generator, constructor injection requires roughly 3 times the effort (more code) and is roughly 3 times more prone to human error than this form of setter injection:
public class SomeMediator
[Inject] public var someView:SomeView;
[Inject] public var someProxy:SomeProxy;
[Inject] public var someService:ISomeService;
public function SomeMediator()
Constructor injection requires twice the number of declarations and an extra assignment step.
public class SomeMediator
private var someView:SomeView;
private var someProxy:SomeProxy;
private var someService:ISomeService;
public function SomeMediator( someView:SomeView, someProxy:SomeProxy, someService:ISomeService )
this.someView = someView;
this.someProxy = someProxy;
this.someService = someService;
Let’s consider what Misko deems the biggest problem with setter injection: forgetting dependencies.
In the case of setter injection, and in the context of the application itself (running on the RobotLegs framework), if we forget to set up a dependency rule, we’ll get a runtime error when the DI framework tries to inject into the Mediator (which happens automatically). This would be exactly the same for Constructor injection. A draw.
If we are instantiating the Mediator manually (in a unit test for example), and we don’t manually inject into it with our DI framework, then setter injection is obviously weaker – we won’t get the DI RTE, we’ll just get a null-pointer exception at some point in our test. Constructor injection is much better here, but something Misko overlooks is that constructor injection is more prone to human error from the inside. We have at least three places where we can forget to properly take care of our dependencies:
- We might declare the dependency in our constructor argument list, but then forget to store the passed value inside our constructor.
- We might declare a property on the class itself but then forget to ask for it in the ctor argument list.
- We might even be so delirious as to incorrectly assign the values in our ctor by excluding “this.”.
None of those mistakes will cause a compile-time error. As with setter injection, we’ll just get a RTE at some point. Again, a draw.
When we consider what a Mediator is, and the fact that Mediators are amongst the biggest candidates for re-factoring (we might start with one Mediator for a complex nested view, for example, and then break it out into several finely grained Mediators later), setter injection becomes even more convenient. Constructor injection requires more effort when dependencies change: 3 places need editing, including the constructor’s method signature – necessitating external changes for any manually instantiated instances.
Another charge against setter injection is that it can be responsible for objects in inconsistent states: directly after construction, and before injection has occurred, the object is not quite ready for use (it’s dependencies are missing). But this isn’t much of a problem when your architectural framework handles that side of things automatically – it instantiates and injects things for you.
Hybrids In Progress, A Sliding Scale
I’d also like to touch on the mixing of injection styles. Some people see this as akin to mixing coding standards in a project – ugly. At the same time, setter injection must be used under certain conditions (circular dependencies for example), so even purists have to mix styles sometimes. I’ve chosen to roll with it, and use what makes the most sense for the case at hand. Looking at actors in the RobotLegs framework, I’m inclined towards:
- Setter injection for Mediators
- Setter injection for Commands
- Constructor injection for Proxies
- Constructor injection for Services
- Constructor injection for non-framework entities
My reasoning is that:
- Mediators and Commands generally have more dependencies and require refactoring more frequently
- Proxies and Services should have very few dependencies
- Non-framework entities are more likely to be created manually
If we look at setter vs ctor injection on a sliding scale, we have:
- setter -> constructor
- convenient -> safe
We can always migrate from setter to ctor injection as classes settle down and their refactoring likelihood decreases:
- setter -> constructor
- sketchy -> final
Mediators, by their very nature, are the sketchiest parts of an application: they are tightly coupled both to the application and the view. Commands are coupled to the application. There is very little need to make either of these actors “safe” or “final”, and for flexibility it is wise to leave them “convenient”.
Proxies and Services, however, should not be too coupled to the application. They should be the “safest” most “final” classes in the interests of loose coupling and portability. I would start with setter injection and migrate to constructor injection as those components mature.
This comment, thanks!