Three ways of injecting dependencies

I know, I know, this has already been said at least a million times, but as I see it’s not yet obvious to everyone. So, in real short, let’s talk about three ways of injecting your dependencies and their pros and cons.

OK, first of all: why is dependency injection (DI, or inversion of control IOC) important? The answer is simple; you simply cannot use a lot of powerful test doubling techniques without it. Given a method like this:

public class ResourceHandler {
    public boolean connectToResource() {
        Connection connection = new MyConnection(...);
        connection.connect();
        //...
        return connection.isAlive();
    }
}

Ok, now what happens when you try to get this method under test? You’d probably get some kind of error message telling you that you couldn’t connect to what you wanted to. And that is perfectly valid, since usually you don’t have a database set up for tests (or else your tests are not unit tests anymore).
See the problem? You don’t have any effect on the variable “connection”. You cannot mock or fake your connection, because there is no way to swap the instance created by the call of “new” operator with a mock/fake object. Your tests will not run, you delete your test methods, your coverage drops, eventually everything goes wrong.

So the ways to break the dependency:

Setter-based

Setter-based dependency injection is the most basic method for breaking dependencies. All you have to do is promote your variable to a field, and have a setter on it:

public class ResourceHandler {

    private Connection connection;

    public boolean connectToResource() {
        connection.connect();
        //...
        return connection.isAlive();
    }

    public void setConnection(Connection connection) {
        this.connection = connection;
    }
}

And that’s it. Now you just have to call the setter from somewhere outside the class, and inject your mock/fake object. It’s simple, doesn’t imply any signature change and should be avoided by all means. I mean it; just think about it: what happens if you accidentally forget to call the setter?(And you will, because nothing warns you not to – remember, your code will still compile perfectly). Then you get a nice NullPointerException at runtime.

Method argument-based

This technique involves the following: you pull the variable up to method argument level and have the upper level class pass in the instance to your method:

public class ResourceHandler {
    public boolean connectToResource(Connection connection) {
        connection.connect();
        //...
        return connection.isAlive();
    }
}

//In the higher level class:
ResourceHandler resourceHandler = new ResourceHandler();
Connection connection = new MyConnection(...);
resourceHandler.connectToResource(connection);

This solution is safer than the previous one. As soon as you change the signature of your method, you get a load of error messages in your workspace. You know exactly where and what to change. You simply cannot forget to pass in a parameter, because your build will fail.
However, as you can see, the higher level class also has the the problem of not having any effect on the variable. We now have the same problem, but one level higher. So eventually we have to push the creation of the instance to a top-level factory.

Constructor argument-based

This method is very similar to the previous one. This time the argument is introduced on constructor level; leading to something like this:

public class ResourceHandler {

    private Connection connection;

    public ResourceHandler(Connection connection) {
        this.connection = connection;
    }
    public boolean connectToResource() {
        connection.connect();
        //...
        return connection.isAlive();
    }
}

//In the higher level class:
Connection connection = new MyConnection(...);
ResourceHandler resourceHandler = new ResourceHandler(connection);
resourceHandler.connectToResource();

Passing arguments through the constructor is actually the best technique for breaking dependencies. It has the very cool feature that when you are getting too many constructor arguments, you know for sure you have to refactor and split the class. Anyway, based on the principle of single responsibility, most of the methods defined in a class should use all or most of the constructor arguments.  The rest of the cons/pros described above hold for this method too.

When to use them

  1. Setter based: basically, never.
  2. Method argument-based: in case of utility classes, where you don’t want to create a new instance every time you want to validate/calculate/formulate/etc. something (validate IP addresses, calculate hash values, formulate output messages etc.)
  3. Constructor argument-based: in most of the cases; when your class needs the injected instance throughout its whole lifetime.
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