Constructor Injection vs. Setter Injection
Thursday, February 19, 2009
by Miško Hevery
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.
Great I have instantiated CreditCardProcessor, but is that enough? No, I somehow need to know to call, setOfflineQueue(). This information is not necessarily obvious.
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.
But wait, you are not done you need to set the Username, password and the URL on the database.
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.
Notice we are not done yet since CreditCardProcessor needs a queue and a database, so lets make those.
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.
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.
A big pro for constructor injection is the possibility to build immutable objects out of it.
ReplyDeleteThat's all sounds very good, immutability is good. But, I don't agree that code is easier to read with constructor injection.
ReplyDeleteWhen there are many depended objects, and especially, if they are of the same class it becomes hard to read and to write such code. In your example you have
Database db = new Database("username", "password", "jdbc:....");
It is error prone to construct such object, because all arguments are Strings, and they must be passed in the right order. Compiler can't help here.
With setters it is clear, and there is no ambiguity.
But, in general, I like immutable objects. Perhaps Joshua Bloch Builder pattern can help here.
Database database = new Database.Builder()
.url("url")
.user("user")
.password("pass").build();
I tend to always use constructor injection. I will only use setter injection for optional dependencies.
ReplyDeleteFor example if I want to use a logger when I am not using aspects, instead of requiring an ILogger in the constructor I will have a setter for ILogger.
In my constructor I instantiate the logger with Logger.NullLogger and my container can wire up the real logger implementation at runtime.
Using constructor injection makes it very explicit that certain dependencies are "required" for the object to function.
You are correct that with setter injection you loose focus of this, but using setter injection for optional dependencies creates a little less noise too.
Using Constructor injection for mandatory parameters is really the best scenario.
ReplyDeleteIn order to remove the problem mentioned by serega, a nice practice is to improve your domain by adding additional objects like "Credentials", "DataBaseConnectionInfo" and so on...
These ones may use getters/setters or even better a builder pattern.
You finally have a richer
new Database(credentials,databaseInfo);
constructor with no possible misunderstanding on the order of parameters.
Though I'd like to mention that immutable are really a nice-to-have, but are completely incompatible with the (sadly) widely used javabeans spec... This is one of the reasons why Spring used the setters and no-param constructor, in order to benefit from this spec.
Be my guest and try to expose an immutable object with JaxWS ;)...
serega, that can be solved by using keyword arguments, if the language supports them.
ReplyDeletedb = Database(url="url", user="user", password="pass")
You forgot to mention a serious advantage of setter injection. It is the ability to ignore defaults.
ReplyDeleteUsing constructor injection, you either get an explosion of constructors (where each constructor takes a certain subset of all arguments), or you are forced to provide values for attributes that where the defaults are sufficient.
If you are using a DI framework (like Spring, maybe like Guice, but never used it yet), you are shielded from object construction anyway, so it is largely irrelevant which form you use.
If you are not using a DI framework, then I, too, am partial to Josh Bloch's builder.
I am totally pro constructor injection, as it enforces consistent objects, and prevents null pointers (forgot to set an object).
ReplyDeleteHowever, how do we keep our constructors lightweight? If we do constructor injection, won't we be tempted to violate http://misko.hevery.com/code-reviewers-guide/ rule #1?
@Andrew: keyword arguments let you skip default too. You could have 5 arguments and only give the last one.
ReplyDeleteOf course if the language you're using doesn't support them then setter injection might be the least ugly option.
I'll second the recommendation to use builders as a way to keep the setup code readable (especially in languages such as Java where you don't have default or named parameters) and have immutable objects without exposing a growing number of constructors.
ReplyDeleteSpring btw supports circular dependencies even when using constructor injection!
I would be very curious to hear the author's thoughts about dependency injection with OSGi. I don't get the impression that OSGi is used within Google. If not, I was wondering if the author could comment on its relative merits for these problems.
ReplyDeleteConstructor Injection has another plus: assuming the constructed component implements an interface, you can refer to the freshly built object by the interface and "forget" the implementation type after construction. With Setter Injection to make use of the setters you must assign the instance to a reference of the actual implementation type.
ReplyDeletee.g.
CreditCardProcessor processor = new CreditCardProcessorImpl(database, ...)
vs.
CreditCardProcessorImpl processor = new CreditCardProcessorImpl();
processor.setDatabse(...);
If you're worried about mixing up the order of string parameters, one option is to turn some of them into a parameter object, e.g.:
ReplyDeleteDbAuth auth = new DbAuth("user", "pass");
String url = "jdbc:...";
new Database(auth, url);
@serega If you have that many dependencies, then it's most likely that either your class is too large or that some of your dependencies should be packaged up into a higher level structure.
ReplyDeleteIf I have a range of configurations, one pattern I like is to keep DI in the constructor very simple (just set the final fields) and use factory methods to create the different versions. Not least because I can give them descriptive names, rather than just different constructors.
ReplyDeleteThe best argument in favor of "constructor injection" that I don't see here yet is that it's vastly preferable to construct complete objects. This is an invaluable invariant, and the constructor is the place to establish that invariant. I can't exaggerate how much easier life is if you don't have to ask yourself whether that object is really ready to be used, or if it's in some weird half-assembled state with unpredictable, nonsensical behavior.
ReplyDeleteAs for the issue that was mentioned about constructor arguments that are hard to tell apart because they're all strings (or ints, or even worse bools), fair point. One solution is to be sensitive in your design and regard excessive numbers of parameters as a code smell.
Another approach that can be taken sometimes is to create trivial wrapper types -- if e.g. an accountId is represented as a string, that doesn't mean it should be passed around as one; that just tempts us to accidentally try and make it interoperate or interchange with other strings that are *not* accountIds. I wouldn't do this universally, but it can be worth it for commonly-used entity types that are passed around a lot.
I just want to point out that I do prefer "constructor injection" and always try to use it in my code. There are many valid points mentioned the post and in the comments. I just want to add that immutable objects make working with concurrent code (which I often do) a lot easier, because you don't need to do any synchronization. When you publish an object with all final fields JVM guarantees that those fields will be initialized.
ReplyDeleteBack to readability.
Adam Olsen:
Java does not support defaults in the arguments.
zepaq and Luke:
I agree that introducing rich wrappers can solve the problem of ambiguity and readability, but you are not going to do that for each String, int and bool in your code. Often you work with existing API that use primitives, so you will have to wrap that API to make it "richer". Unless you are a solo developer, that can make the problem worse, because the readers of you code will have to learn your new "rich" API.
nice post
ReplyDelete