Inversion of Control (IoC) is an essential tool for any software designer’s design toolbox because it’s often a great way to break dependencies between classes and promote loose coupling.  IoC is vital for doing Test Driven Development without pulling out all of your hair in frustration. 

 

This was originally published on Jeremy's blog in September of 2005, but the principles still apply equally today.

 

 

Real Life Example

 

I’m doing an interesting refactoring early this week on some existing code.  I apologize in advance for being a little vague, but the NDA thingy gets in the way a bit here.  The product that my team works on is basically a B2B hub between sender companies (spokes) and receiving companies. 

 

The application:

  1. Receives a message in one of a handful of industry standards and 3rd party formats
  2. Converts the data into a canonical data format
  3. Performs a variety of business validations
  4. Translates the results into yet another industry standard for transmittal to another downstream system

 

The translation code is obviously critical to the system, so it’s very important that the automated testing is rock solid.  Moreover, that code would be extremely useful in many other contexts throughout our enterprise.  The code works just fine and there is a robust suite of automated tests ensuring that the code continues to work.  I had tried in the past to adapt this code inside of an automated regression test tool without any success because it had too much “tail.”  What I mean by this is that the code can only function with some specific dependencies on a couple of different data sources that weren’t necessarily easy or even possible to recreate without bringing in a large fraction of the system.  Using the translation code requires some data access code which in turn requires some configuration code and external settings in a Registry (We’re going to make this go away too.  I detest using the Registry for any configuration, much less as a Plugin strategy.).

 

In an ongoing effort to speed up a slow (15-20 minutes) build, I managed to break the integration tests for the translation classes and had an opportunity to look much closer at this code.  Here’s roughly what I found:

 

      public class Translator1

      {

            private readonly IDataSource _source;

 

            public Translator1(IDataSource source)

            {

                  _source = source;

            }

 

            public Translator1()

            {

                  _source = new CompanyDataSource();

            }

 

            public XmlDocument TranslateFlatFile(byte[] contents, string senderId, string receiverId)

            {

                  XmlDocument document = this.startNewXmlDocument();

 

                  CompanyInfo sender = _source.FetchCompanyInfo(senderId);

                  CompanyInfo receiver = _source.FetchCompanyInfo(receiverId);

 

                  this.createSenderNode(sender, document);

                  this.createReceiverNode(receiver, document);

 

                  // Do a lot of other stuff

 

                  return document;

            }

 

            private void deepInTheBowelsOfTheClass(string key)

            {

                  string something = DataSourceSingleton.Instance.LookupValue(key);

                  // do something with "something"

            }

 

            [OTHER METHODS]

      }

 

 

And the test fixture looks a bit like this:

 

 

      [TestFixture]

      public class Translator1TestFixture

      {

            [Test]

            public void WriteTheSenderNode()

            {

                  // The sender and receiver data is assumed to be in the database -- somewhere

                  Translator1 translator = new Translator1();

 

                  byte[] contents = this.readFlatFile();

 

 

                  XmlDocument document = translator.TranslateFlatFile(contents, "ID00001", "ID00210");

                  XmlNode senderNode = document.DocumentElement.SelectSingleNode("Sender");

                  Assert.AreEqual("Austin", senderNode["City"].InnerText);

            }

 

            private byte[] readFlatFile()…

      }

 

 

So what’s so terribly wrong with this code?  From my standpoint there are several problems.

 

 

Refactoring to Inversion of Control

 

My thought on the translation code is to apply Inversion of Control to the translation code to remove the database dependency.  Let some other class be responsible for running out and fetching the receiver and sender information and “push” it into the translator.  I could have abstracted the data source class as an interface and used Constructor Injection to push alternative classes or mocks into the Translator2 class.  My thinking in this case is to simplify both the reuse of the translation code and eliminate the mocking overhead (~70 automated tests) by eliminating the interaction with a data source class altogether.

 

The code below is a skeleton of the approach I’m going to take to refactor our translation code (someday).  Here’s how I think the new code stacks up to the old code.

 

 

      public class Translator2

      {

            public Translator2()

            {

            }

 

            public XmlDocument TranslateFlatFile(byte[] contents, CompanyInfo sender, CompanyInfo receiver )

            {

                  XmlDocument document = this.startNewXmlDocument();

 

                  this.createSenderNode(sender, document);

                  this.createReceiverNode(receiver, document);

 

                  // Do a lot of other stuff

 

                  return document;

            }

 

 

            #region other methods

      }

 

      [TestFixture]

      public class Translator2TestFixture

      {

            [Test]

            public void WriteTheSenderNode()

            {

                  CompanyInfo sender = new CompanyInfo();

                  sender.City = "Austin";

                  // set other sender properties

 

                  CompanyInfo receiver = new CompanyInfo();

                  // set receiver properties

 

                  byte[] contents = this.readFlatFile();

                  Translator2 translator = new Translator2();

 

                  XmlDocument document = translator.TranslateFlatFile(contents, sender, receiver);

                  XmlNode senderNode = document.DocumentElement.SelectSingleNode("Sender");

                  Assert.AreEqual(sender.City, senderNode["City"].InnerText);

            }

 

            private byte[] readFlatFile()…

      }

 

 

All I’ve really done is move the responsibility for fetching data out of the database into another class.  It’s a small change but it had a positive impact on both the code and the testing.  If you think about it, you could actually say that software design boils down to “Where should this code go?”  I think there’s a watershed moment in every developer’s career when they start look more at their code as a structure and less as a bag of statements. 

 

Other Examples

 

We’re taught that the key to safe lifting is “push, don’t pull.”  I like to think of Inversion of Control as applying this mantra to code.  IoC is fairly common, and much more so with Test Driven Development practices.  Here are some other examples of IoC you might run into.

 

 

      public class PullClass

      {

            private int _threshold;

 

            public PullClass()

            {

                  _threshold = int.Parse(System.Configuration.ConfigurationSettings.AppSettings["Threshold"]);

            }

      }

 

      public class PushClass

      {

            private readonly int _threshold;

 

            public PushClass(int threshold)

            {

                  _threshold = threshold;

            }

      }