As an Agile Coach, Miško is responsible for teaching his co-workers to maintain the highest level of automated testing culture, allowing frequent releases of applications with high quality. He is very involved in Open Source community and an author of several open source projects. Recently his interest in Test Driven Developement turned into http://TestabilityExplorer.org with which he hopes will change the testing culture of the open source community. Misko is a DZone MVB and is not an employee of DZone and has posted 38 posts at DZone. You can read more from them at their website. View Full User Profile

Constructor Injection vs. Setter Injection

02.20.2009
| 24300 views |
  • submit to reddit

There seems to be two camps in dependency-injection: (1) The constructor-injection camp and (2) the setter-injection camp. Historically the setter-injection camp come from spring, whereas constructor-injection camp are from pico-container and GUICE. But lets leave the history behind and explore the differences in the strategies.

Setter-Injection

The basic-ideas is that you have a no argument-constructor which creates the object with “reasonable-defaults” . The user of the object can then call setters on the object to override the collaborators of the object in order to wire the object graph together or to replace the key collaborators with test-doubles.

Constructor-Injection

The basic idea with constructor-injection is that the object has no defaults and instead you have a single constructor where all of the collaborators and values need to be supplied before you can instantiate the object.

At first it may seem that setter injection is preferred since you have no argument constructors which will make it easy for you to create the object in production and test. However, there is one non-obvious benefit with constructor injection, which in my opinion makes it a winner. Constructor-injection enforces the order of initialization and prevents circular dependencies. With setter-injection it is not clear in which order things need to be instantiated and when the wiring is done. In a typical application there may be hundreds of collaborators with at least as many setter calls to wire them together. It is easy to miss a few setter calls when wiring the application together. On the other hand constructor-injection automatically enforces the order and completeness of the instantiated. Furthermore, when the last object is instantiated the wiring phase of your application is completed. This further allows me to set the collaborators as final which makes the code easier to comprehend if you know a given field will not change during the lifetime of the application.

Let’s look at an example as to how we would instantiate a CreditCardProcessor.

CreditCardProcessor processor 
        = new CreditCardProcessor();

Great I have instantiated CreditCardProcessor, but is that enough? No, I somehow need to know to call, setOfflineQueue(). This information is not necessarily obvious.

OfflineQueue queue = new OfflineQueue();
CreditCardProcessor processor
        = new CreditCardProcessor();
processor.setOfflineQueue(queue);

Ok I have instantiated the OfflineQueue and remember to set the queue as a collaborator of the processor, but am I done? No, you need to set the database to both the queue and the processor.

Database db = new Database();
OfflineQueue queue = new OfflineQueue();
queue.setDatabase(db);
CreditCardProcessor processor
        = new CreditCardProcessor();
processor.setOfflineQueue(queue);
processor.setDatabase(db);

But wait, you are not done you need to set the Username, password and the URL on the database.

Database db = new Database();
db.setUsername("username");
db.setPassword("password");
db.setUrl("jdbc:....");
OfflineQueue queue = new OfflineQueue();
queue.setDatabase(db);
CreditCardProcessor processor
        = new CreditCardProcessor();
processor.setOfflineQueue(queue);
processor.setDatabase(db);

Ok, am I done now? I think so, but how do I know for sure? I know a framework will take care of it, but what if I am in a language where there is no framework, then what?

Ok, now let’s see how much easier this will be in the constructor-injection. Lets instantiate CreditCardPrecossor.

CreditCardProcessor processor
        = new CreditCardProcessor(?queue?, ?db?);

Notice we are not done yet since CreditCardProcessor needs a queue and a database, so lets make those.

Database db = new Database(
        "username", "password", "jdbc:....");
OfflineQueue queue = new OfflineQueue(db);
CreditCardProcessor processor
        = new CreditCardProcessor(queue, db);

Ok, every constructor parameter is accounted for, therefore we are done. No framework needed, to tell us that we are done. As an added bonus the code will not even compile if all of the constructor arguments are not satisfied. It is also not possible to instantiate things in the wrong order. You must instantiate Database before the OfflineQueue, since otherwise you could not make the compiler happy. I personally find the constructor-injection much easier to use and the code is much easier to read and understand.

Recently, I was building a Flex application and using the Model-View-Controller. Flex XML markup requires that components must have no argument constructors, therefore I was left with setter-injection as the only way to do dependency injection. After several views I was having hard time to keep all of the pieces wired together properly, I was constantly forgetting to wire things together. This made the debugging hard since the application appeared to be wired together (as there are reasonable defaults for your collaborators)  but the collaborators were of wrong instances and therefor the application was not behaving just right. To solve the issue, I was forced to abandon the  Flex XML as a way to instantiate the application so that I can start using the constructor-injection and these issues went away.

From http://misko.hevery.com

Published at DZone with permission of Misko Hevery, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Jörg Buchberger replied on Fri, 2009/02/20 - 9:42am

;-) You forgot camp (3) field injection (as e.g. in EJB3) and for rounding up your ;-) "historical detour" you could have also contrasted injection with old-school dependency-lookup mechanisms. But hey, thanks for another nice article. Happy coding!

Howard Lewis Ship replied on Fri, 2009/02/20 - 10:44am

I think in a perfect world, you would see only constructor injection (so that the values could be stored in final fields). If you've read "Java Concurrency in Practice" you'll realize this is important.  However, some people want to skate on the Java memory model being forgiving, so Tapestry 5 IoC also supports method injection and field injection (via reflection).

 Interestingly,  for the latter two, I had to add new method annotations to indicate any post-construction methods to invoke; with constructor injection you don't need that ... the object is always in a known state, but with method/field injection, it can be partially constructed but not yet ready to put into production.

Josh Marotti replied on Fri, 2009/02/20 - 3:09pm

Constructor arguments give you the benefit of making the object immutable, so they should be used unless you have a solid reason not to.


That's your whole schpeel put into a single sentence ;)

Bhavani Sankar replied on Sat, 2009/02/21 - 6:42am

Good article !!!!

Stefan Schulz replied on Sat, 2009/02/21 - 12:48pm

Nice comparison, but ... the only reason to use setter-injection is the lack of Metaclasses in Java. If one could define meta-interfaces, for example, which also allows to define APIs for constructors or method-factories, there would be no need for the unsafe use of setter-injection. As an example, think about having multiple classes instead of one Database class, one of which is chosen at runtime.

Kerdudou Ronan replied on Fri, 2009/04/17 - 3:12am

We use something like :

public class MyClass {
private Object obj;
MyClass() {}
public MyClass(Object arg){
this.obj = arg;
}
public Object getObj() {
return this.obj;
}
void setObj(Object arg) {
this.obj=arg;
}
}

 The framework that need camp(1) can do it but developpers are using camp(2) :-)

 

 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.