That's all sounds very good, immutability is good. But, I don't agree that code is easier to read with constructor injection. When 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.
For 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. In 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 ;)...
You forgot to mention a serious advantage of setter injection. It is the ability to ignore defaults.
Using 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).
However, 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?
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.
Spring 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.
Constructor 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.
e.g.
CreditCardProcessor processor = new CreditCardProcessorImpl(database, ...)
vs.
CreditCardProcessorImpl processor = new CreditCardProcessorImpl(); processor.setDatabse(...);
@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.
If 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.
The 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.
As 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. Back 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.
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