- Injection through constructor - most clear and straight way for coding.
But not always this way can be used. Object can't be passed in through constructor, when we need more than one instance of the object. Also, it's not good for performance to create objects, which are optional (used inside 'if' clauses). We can fix it by using factories, but not when we need only one instance of the optional object (because we will create instance of factory anyway).
- Service Locator (SL, or Service Container, or Objects Manager, or Resources Manager).
At first look, this pattern can create very tight coupling, because all objects, which will require Service Locator, will be dependent of existing of this object. For standalone modules it could look even as forbidden pattern. But we can use this pattern in smart way: object should not require whole SL class, but only necessary Interface.
- Setters - most "dirty" way.
Little explanation of this short word: object have two methods for injection, getter and setter. Getter it's method to get instance of the needed object. Setter is method to set this instance. Why this way is dirty? Because, when we call method, we don't know dependencies of this method. If there is no comments for this method, we have to read source code of this method to know, objects of which classes are used in this method. So, we have to remember, that we need to set some setters before calling this method - that's why it is dirty way. But, we can create one little exception for the Setters method, to make it usable. We can declare mutual defended object in getter, so if instance was not set through the setter, instance of default class will be created and returned. Yes, it's not best way and we should avoid it when it possible, because it means using 'new' keyword. But we still able to set mock-object for testing.
So, my recommendations, how to choose variant of DI:
- If the instance of the object is not optional - require it in the constructor.
- If the instance is optional, require in the constructor the Interface with method for creating the instance.
- If you want to remove argument from constructor by some reasons,and if after doing this you will achieve some very good result, you can use Setter with Getter and default value.
Martin Fowler: Inversion of Control Containers and the Dependency Injection pattern
Miško Hevery: Writing Testable Code