Constructor vs. Field injection

I was interviewing a candidate the other day; we were talking about Spring, when I asked him if he preferred constructor or field injection. Typically people pick a side; either the first or the second one, so I was pretty surprised when the candidate told me that it depends on the context. And actually, this is the most awesome answer (in my opinion, of course) one can give to this question.

A lot has already been written about this topic. Most of the articles (and several people I know) state that not injecting all your dependencies through the constructors you are doing it all wrong, as you are hiding your dependencies (This makes perfect sense, but only up to a certain limit). Do these people then actually say that you have no way to write code of good quality based on the EJB technology? There you cannot use constructor injection at all – since you must provide a no-arg constructor that will be called by the container. Can anybody say that every each such system is rubbish and should be thrown away? I’m quite positive that we could find awesome EJB systems without an exhaustive search.

On the other hand, however, in Spring you are free to inject anything through your constructors. Is it than the only one right way to do dependency injection? Should the field based option just left out starting from the next releases of the framework and buried forever?I’d say no – and not only for backwards compatibility reasons. There are many cases when constructor injection produces better results than field injection; but not always. 

Let’s just take two examples. In the first one we are producing the most awesome BDD framework ever. There is only one thing left for us to do, develop a reporting module that is capable of producing test results in various formats (XML, HTM and plain text, for example).  We can come up with something like this:


public class ReportHandler {

  private Reporter reporter;

  public ReportHandler(Reporter reporter) {
    this.reporter = reporter;
  }

  public ReportContents produceOutput() {
    //...
    reporter.getReport()
    //...
  }

  public void dumpOutput() {
    //...
    reporter.persistReport();
    //...
  }
  //...
}

Constructor based injection is clearly the right choice here. With field-based injection we couldn’t even decide what kind of reporter to inject – that is an another level of abstraction.

Let’s now suppose that we are writing a simple text parser utility; it needs to replace placeholders of format #{…} with actual values, replace end of line characters to Linux style and remove all lines that start with three stars. In this case these three responsibilities may very well be factored out to different classes. Now, we have two possibilities:

Nr. 1

public class Parser {

 public Parser(PlaceholderResolver resolver, EndingsReplacer replacer, CommentsDeleter deleter) {
   this. resolver = resolver;
   this.replacer = replacer;
   this.deleter = deleter;
 }

 public Text resolveProperties(...) {
   //...
   Text text = resolver.resolve(...);
   //...
 }

 public Text deleteComments(...) {
   //...
   Text text =  deleter.removeComments(...);
   //...
  }

 public Text replaceEndings(...) {
   //...
   Text text = replacer.replaceEndings(...);
   //...
 }
}

or Nr. 2

public class Parser {

 @Inject
 PlaceholderResolver resolver;
 @Inject
 EndingsReplacer replacer;
 @Inject
 CommentsDeleter deleter;

 
 public Text resolveProperties(...) {
   //...
   Text text = resolver.resolve(...);
   //...
 }

 public Text deleteComments(...) {
   //...
   Text text = deleter.removeComments(...);
   //...
  }

 public Text replaceEndings(...) {
   //...
   Text text = replacer.replaceEndings(...);
   //...
 }
}

The problem with the first version is that we are injecting dependencies through the constructor which are not really dependencies for the whole Reporter object, but for different methods only. This way we are polluting the constructor with dependencies that will be used at only one or two places.

People overly into constructor injection, commonly come up with problems like:

The second version is untestable, as we have no control over the three dependencies. This, in my opinion, is hypocrisy; we’ve all learnt so far that putting in a protected setter just for a unit test is not an evil thing. Having everything tested thoroughly is worth a such a method or two.

“One could call those setters within the package” they argue further… Well, perhaps we should treat each other like professionals who know what they’re doing and not malicious code-monkeys, which try to make the biggest harm possible.

“@Inject, a Spring related dependency is spread in my application”. Well this is not true, as @Inject is in package javax.inject, so this is a javax dependency. Anyway, let’s pretend we have @Autowired instead of @Inject; I’ve never seen a mature application that was ported from Spring to some other IoC container; if you use Spring, you inevitably have it as a dependency. It is, perhaps, not worth the effort to keep Spring-related stuff in the config files only, as it can keep you from reinventing the wheel.

Single responsibility principle is a two ways street. As such, it can be violated two ways: you either create objects with way too many responsibilities, or none. That is, you create your objects so dummy that they have no real logic in them. It’s like someone was like: “Introduce yourself, say that your name is Steven”. Shouldn’t it rather be “Introduce yourself” only, with the responsibility of telling your name left to you? I think it should. Not to mention that in the case of excessive constructor-injection you will -pretty fast- face with monster config files (be it Java or XML) – objects putting together other objects in a long, ugly chain.

In summary, field and constructor injection are two complementary technologies. Using one does not rule out the other; one should, however, apply common sense when dealing with this issue, and should not be overly biased toward one or the another.

Advertisements

Author: tamasgyorfi

Senior software engineer, certified enterprise architect and certified Scrum master. Feel free to connect on Twitter: @tamasgyorfi

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s