In Defense of Dependency Injection

 Mar, 16 - 2010   no comments   Uncategorized

I’ve been rolling the RobotLegs framework into a new project, but not all the team members are sold on the idea, mostly because it represents third-party code that’s complex and difficult (impossible?) to understand just by reading over the source.

I realized I was having a difficult time articulating the advantages to the approach, and specifically, the fact that the closest alternatives are every bit as complex and headache-inducing once you get down into the details.

So, what follows is a bit of a thought experiment using a very simple case. Hopefully, by the end, you’ll see that what’s true for a single, simple case is also true for multiple, complex cases, and in the end, it actually makes good sense to have a framework handle these things.

Imagine first, a Main class. There’s only one Main across the entire codebase. Now imagine classes A, B, C, etc (there are a lot of them) that all use a ColorManager class. A, B, C are separate SWFs, so it makes sense for ColorManager to be compiled into Main, and somehow linked into the A, B, and C modules, but not compiled into them. This in itself can be a bit tricky, and there are a few different approaches, with assorted pros and cons.

Our first thought is to compile against a linked SWC containing the concrete ColorManager class. That will keep the actual implementation out of our modules, but any change to ColorManager will require re-compiling every SWF that uses it. Less than ideal.

We could say:

public class A
{
  private var colorManagerClass:Class = getDefinitionByName(“ColorManager”);
  private var colorManager:IColorManager = new colorManagerClass();
}

It gets the job done, and it’s easy to understand. As long as the interface stays in synch, it’s not too hard to make changes to the concrete ColorManager class without recompiling the module (and all the others). That synched interface is a pretty big caveat, but we’ll find that most (all?) approaches share it. We could forego the interface completely, but at the expense of loosing very valuable strong-typing and code completion.

The downside would be that the concrete class absolutely must be “ColorManager” — even though we’ve gone to the trouble of creating an interface (which would/should/could allow other implementations) were stuck with the one-and-only ColorManager returned by getDefinitionByName. The simplest way to make this dynamic? Pass in the String representing the class name. You know what that’s called? Dependency Injection. If you’re willing to pass in a String, you might as well pass in the class itself.

Another quibble with this technique is that it relies on something known as a “magic string.” The compiler doesn’t have a way of warning you that this class does or doesn’t exist, or that it correctly implements the IColorManager interface. You’ll need to rely on runtime warnings for that. It’s very easy to misspell or miscapitalize the package/classname breaking everything.

Ok, my (completely competent) team member suggests, we can provide a setter for the class. That might look like:

public class A
{
  private var colorManager:IColorManager

  public function setColorManagerClass (colorManagerClass:IColorManager)
  {
    this.colorManager = new colorManagerClass();
  }
}

Again, this is merely another way of doing Dependency Injection. The real question is whether you’ll be using a framework to provide dependencies automatically. We’ll come back to address this separately.

What about having our class request the class at runtime, rather than relying on the fact that it will be injected. Something like this:

public class A
{
  private var classLocatorServiceInstance:ClassLocatorService;
  private var colorManager:IColorManager;

  public function initColorManager()
  {
    var colorManagerClass:Class = classLocatorServiceInstance.getConcreteClass(IColorManager);
    this.colorManager = new colorManagerClass();
  }
}

This implementation, while quite valid, raises more questions than it answers. Who calls initColorManager? How can it be sure the ClassLocatorService is ready to provide ColorManager? And just where does this ClassLocatorService come from any way? It’s a singleton? How is that enforced? Did you notice how we’re requesting the concrete class? Via its interface. We’ve got the same problem with allowing only one implementation of IColorManager. In order to allow multiple implementations, we’re back to using a magic String.


Related articles


Leave a Reply

Your email address will not be published. Fields with * are mandatory.