Using Dependency Injection To Increase Flexibility

posted on 03/28/08 at 12:31:15 am by Joel Ross

Tonight, I was thinking about a side project I've been working on, and I realized that I had used Dependency Injection (DI). I'm pretty sure that DI is my favorite pattern, so I decided to share on twitter. Steve Wright responded:

I wonder what all the DI hubbub is about, do I even need it?  I keep asking, and I think it's time to figure it out

Seeing that, I figured I'd share how I used DI and what it accomplished for me. I'm sure Steve knows what DI is, but inspiration has to come from somewhere, right?

First, a little set up. I wrote some code that interacts with the Twitter API to send and receive messages. Without DI, it looked something like this:

   1:  public class User
   2:  {
   3:     private string _userName;
   4:     public string UserName
   5:     {
   6:        get { return _userName; }
   7:     }
   8:   
   9:     public User(string userName)
  10:     {
  11:        _userName = userName;
  12:     }
  13:   
  14:     public void SendMessageTo(string message)
  15:     {
  16:        TwitterAPI twitter = new TwitterAPI();
  17:        twitter.SendTweet(String.Format("@{0} - {1}", this.UserName, message));
  18:     }
  19:  }

This doesn't seem too bad at first glance. Yes, you could probably modify this a bit to make the TwitterAPI variable a class level variable and that would be a little better - you wouldn't have to instantiate it every time you wanted to send a message. But there's still a problem. What if you wanted to test this without spamming twitter? Or instead of twitter, you wanted to send messages to Pownce or Jaiku? Or your blog? How would you do that?

The issue is that the code above has a dependency on the TwitterAPI class, so you're pretty much stuck. What I've seen done (which makes me cringe) is to add switch statements where each case contains code to post to a different service. What happens when a new service comes out? Or you want to post to two services? You're stuck in a maintenance nightmare because this class has to change every time a new service comes out.

Luckily, dependency injection solves this problem. Instead of the User class having a dependency on TwitterAPI (or PownceAPI, JaikuAPI, etc.), it only depends on an interface, and the concrete implementation of that interface is injected into the class. In this case, the interface is pretty simple:

   1:  public interface IServiceAPI
   2:  {
   3:     void SendMessage(string message);
   4:  }

Then we can change our User class to use the interface, and have it injected into the instance through the constructor:

   1:  public class User
   2:  {
   3:     private IServiceAPI _service;
   4:     public IServiceAPI Service
   5:     {
   6:        get { return _service;}
   7:     }
   8:   
   9:     private string _userName;
  10:     public string UserName
  11:     {
  12:        get { return _userName; }
  13:     }
  14:   
  15:     public User(string userName, IServiceAPI service)
  16:     {
  17:        _userName = userName;
  18:        _service = service;
  19:     }
  20:   
  21:     public void SendMessageTo(string message)
  22:     {
  23:        service.SendMessage(String.Format("@{0} - {1}", this.UserName, message));
  24:     }
  25:  }

This effectively makes our User class service agnostic. I can now implement the IServiceAPI for Twitter, Jaiku, Pownce, an IM client, the console window - or any combination of those. Note that I changed the method name in the service from SendTweet to SendMessage. If you don't have control over your service implementations, then you can use the Adapter pattern to make that work.

The best part about using DI is that this User class can work with services that haven't even been thought of. As long as the service supports the IServiceAPI class (or an adapter can be written to simulate that support), then this class is already prepared to work with it.

I used this model so I could have all of the output to a console window instead of actually posting it, and I controlled which IServiceAPI instance to pass in via a config setting. In the actual implementation, I had a get method as well. The real one (against twitter) did XML requests, while my test one grabbed input from the console window. It made testing it very easy, and didn't require a lot of extra coding - especially using CodeRush to do some of the dirty work, like extracting interfaces, adding parameters to methods, etc.

I haven't used any formal DI frameworks, but that's mainly because I haven't come across a situation yet where I couldn't just code it up by hand. I've seen one in action, so I know they have their place, but I just haven't felt compelled to use one. Still, even with hand coding, Dependency Injection can really help write code that is flexible and resilient to changes in the future.

Categories: Development, C#