Monday, March 7, 2011

What the heck is dependency injection?

In my travels through the Java world over the years, there have been some concepts which I've had trouble finding a simple, succinct definition for. One of them is "dependency injection" (DI), which is one of the hot Java concepts of the last few years. Let me try explaining what it is and why it's useful.

In the normal way of working with Java, you build your objects with constructors. A lot of the times, they need to build other objects they need.
class RouletteBall {}

class RouletteWheel {
  private RouletteBall ball;
  public RouletteWheel() {
    ball = new RouletteBall();
  }
}

class RouletteTable {
  private RouletteWheel wheel;
  private int numberOfSeats;
  public RouletteTable (int n) {
    numberOfSeats = n;
    wheel = new RouletteWheel();
  }
}
Very straightforward. When you ask for a new RouletteTable(), that constructor creates the necessary RouletteWheel, which in turn creates the necessary RouletteBall. The higher-level classes create their own dependencies.

There's another way to do this.

class RouletteBall {}

class RouletteWheel {
  private RouletteBall ball;
  public RouletteWheel (RouletteBall b) {
    ball = b;
  }
}

class RouletteTable {
  private RouletteWheel wheel;
  private int numberOfSeats;
  public RouletteTable (RouletteWheel w, int n) {
    numberOfSeats = n;
    wheel = w;
  }
}

// then you do this
RouletteWheel wheel = new RouletteWheel (new RouletteBall());
RouletteTable table = new RouletteTable (wheel, 8);
Oh snap, guess what, we just added dependency injection. But keep reading though, there's more to this.

The constructors here have been changed to take in the dependencies from outside, instead of generating them internally. The dependencies are supplied, or injected, into the class instances. Hence, dependency injection.

The term "inversion of control" (or IOC) is often applied to this sort of thing. The "control" refers to control over object creation, and the location of that control has been "inverted" from inside the classes to outside of them.

This doesn't seem particularly earth-shattering, and really it isn't at this point. Like a lot of design patterns, it's just a good idea, one that comes from the experience of many smart people working with object-oriented languages. It turns out that employing dependency injection gives you lots of flexibility.

For example, say we augmented our RouletteWheel class to be either European style (single-zero) or American style (double-zero).
class RouletteWheel {
  private RouletteBall ball;
  private boolean doubleZero;
  public RouletteWheel (RouletteBall b, boolean dz) {
    ball = b;
    doubleZero = dz;
  }
}
The first form of the RouletteTable class has a problem now, because its constructor needs to specify a style for the table's wheel. You'd have to add another parameter to the constructor, or maybe a second constructor. There are several ways to cope, but it involves some changes.

The second form of RouletteTable has no trouble with this change, because it just takes in whatever RouletteWheel instance it's handed. No code changes! This is also great because it encapsulates the details of how a RouletteWheel is created; the RouletteWheel class doesn't need to know or care about that.

I'm going to stop here for now. There's more to talk about, but I'd rather let the basic concept of dependency injection sink in. Next time I'll discuss how some frameworks support dependency injection and can help you out.

No comments:

Post a Comment