May 7, 2008

Breaking Dependencies using Dependency Injection

Let's assume you've done some reading recently and heard talk of this Loosely Coupled Architecture thing and how it's meant to help keep an application maintainable, make it more readily changeable and will assist in the creation of unit tests .   You know that it's the right way to go and you can see all the benefits of it. You even think "I should do this on that application at work", but every time you look at the code at work and wonder what space monkeys were hired to write the code in the first place and you think about how tightly bound everything is it all starts to look too hard, so you give up and get back to working with things as they because the problem seems insurmountable.

Now a loosely coupled architecture is at it's heart just another way of saying object oriented design.  I'm sure you'll remember from when you learnt all that Object Oriented Programming stuff that there are some basic principle for OO Programming, namely:

Abstraction, Encapsulation, Polymorphism and Inheritance

And there are also some OO Design principles that sit alongside the OOP principles:

Encapsulate what changes

Favour composition over inheritance

Program to interfaces not implementations

Depend on abstractions, not concrete classes (dependency inversion)

A class should have only one reason to change (single responsibility)

Your work code and the "this problem is too big" feeling doesn't have to be the case.  It's just an indication that you've got legacy code and that it isn't well designed.  The good news is that by following some basic steps you can refactor a legacy, tightly-bound application into one with a loosely coupled architecture, though it will take some time (think many months, not weeks).

There are two basic methods for doing this.  The first is Extract & Override (see Roy Osherove's blog entry) and the other is Dependency Injection.  This is what I'll focus on here.

First, let's have a look at some code from a simple sales application:

    public class Sale
{
private List<SaleLine> saleLines = new List<SaleLine>();
private decimal totalTax;
private decimal totalValue;
...
public bool Add(string inputLine)
{
SaleLine saleLine;

saleLine = InputParser.ProcessInput(inputLine);
if (saleLine == null)
return false;
saleLines.Add(saleLine);
...

Here we see that a sale has multiple sale lines associated with it.  Pretty normal, and something you'll see in many code bases the world over, but we're breaking some of the OOD rules - we're programming to implementations not interfaces and we're using a static method (on the InputParser) which means we're tightly coupled between the Sale object and the SaleLine object.


We're also doing something else that's a no-no.  The Add method is coordinating a whole lot of work to convert an input string into a SaleLine object before adding it to the saleLines collection, whichmeans the class does more than one thing. 


Let's have a quick look at the ProcessInput method on the InputParser class as well

        public static SaleLine ProcessInput(string input)
{
SaleLine saleLine;
...
// create the sale line
saleLine = new SaleLine(quantity, name, price);
return saleLine;
}

What we have here is another tight coupling.  We're creating saleline objects from within the method (the new SaleLine() call) which means we have a tight dependency between the saleline class and the InputParser class.  If we wanted to change the SaleLine to some other class, we'd have some refactoring to do.


 


So let's make some changes...


Lets start by creating interfaces for all of our concrete classes, for example:

    public class Sale : ISale
{
private List<ISaleLine> saleLines = new List<ISaleLine>();

Now, if our definition of what a sale line is changes we don't have to make any changes in the Sale object.  Nice.


Let's also change the input parser so it doesn't create Sale objects directly.  We should do that through the use of a factory pattern implementation, such as the following:

    public class SaleLineFactory : ISaleLineFactory
{
public ISaleLine GetNewSaleLine(int lineQuantity, string name, decimal unitPrice)
{
return new SaleLine(lineQuantity, name, unitPrice);
}
}

And in our InputParser we change the code as follows:

            saleLine = saleLineFactory.GetNewSaleLine(quantity, productName, price);

Good. So now we're not actually creating the SaleLine object directly in our code.  This is a good thing as it breaks the tight coupling between the classes, but doesn't it just move the dependency from the SaleLine class to the SaleLineFactory class?


It would if we just added saleLineFactory = new SaleLineFactory() to the InputParser, but not if we pass an instance of the saleLineFactory class to the InputParser.  This is the Dependency Injection pattern at work.


Now, while we're working in the InputParser class we also have the opportunity to fix another tight coupling.  That between the Sale class and the InputParser.  Let's change the input parser so that we're no longer a static class, but rather a normal class (in a real world situation this could be complex) and let's pass to the ProcessInput method the Sale we want the new line to be added to.  We can then change the sale object so that it's Add method just adds ISaleLine objects instead of being passed a string and then calling to the parser.  Something like this:

    public class Sale : ISale
{
private List<ISaleLine> saleLines = new List<ISaleLine>();
...
public bool Add(ISaleLine saleLine)
{
if (saleLine == null)
return false;
saleLines.Add(saleLine);

public class InputParser : IInputParser
{
private ISaleLineFactory saleLineFactory;
public InputParser(ISaleLineFactory saleLineFactory)
{
this.saleLineFactory = saleLineFactory;
}

public void ProcessInput(string input, ISale sale)
{
ISaleLine saleLine;
...
saleLine = saleLineFactory.GetNewSaleLine(quantity, name, price);
sale.Add(saleLine);
return;
}

Much cleaner.  We now have no hard dependencies between our classes and we are programming to interfaces not concrete classes. 


But we're not done yet.  We still have to create a saleLineFactory class, an InputParser class, a Sale object, etc for us to be able to do any real work.  Where/when/how do we do all of this?


This is where an Inversion of Control (IoC) container can come in handy.  We could just create these objects manually at application startup, but using an IoC container is much easier.  An IoC container lets you map interfaces to classes, i.e. to say that InterfaceX is implemented by ClassY and the container will resolve these dependencies for you.  Most IoC containers let you do a whole lot more as well (Aspects for example).  For this example we'll use the Castle Windsor container to control our object creation for us:

        static void Main(string[] args)
{
IWindsorContainer container = new WindsorContainer(new XmlInterpreter());

IInputControl inputter = container.Resolve<IInputControl>();
IInputParser parser = container.Resolve<IInputParser>();

ISale sale;
string input;

sale = new Sale();
input = inputter.GetInput();
while (!inputter.EOF())
{
parser.ProcessInput(input, sale);
input = inputter.GetInput();
}

And here's the configuration file:

    <configSections>
<
section name="castle" type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />
</
configSections>

<
castle>
<
components>
<
component id="prompt.input" service="Sales.IInputControl, Sales"
type="Sales.PromptInputControl, Sales" />
<component id="input.parser" service="Sales.IInputParser, Sales"
type="Sales.InputParser, Sales" />
<
component id="salelinefactory" service="Sales.ISaleLineFactory, Sales"
type="Sales.SaleLineFactory, Sales" />
</
components>
</
castle>

Now the astute among you will immediately ask why I didn't put the Sale or SaleLine objects into the container?  The answer is fairly simple - they're not good candidates for objects to place in the container.  You want to put things in the container that are service classes, not data classes.  The definition of a sale and saleline will be fairly static, plus the use of the ISaleLineFactory lets us control which ISaleLine implementation we want to use. it's also why the factory is one of the classes the IoC container is aware of - a factory class is a service that the application uses.


You might also ask, why am I using sale = new Sale() in my sample code?  Well, I could have written an ISaleFactory and used that instead (and in a real world system it's what I'd recommend) but this is a sample app, so I beg your forgiveness :-)


Finally, the really astute amongst you will notice that I never call Resolve for the ISaleLineFactory interface and I don't provide parameters for the IInputParser class when it's resolved.  So how does it get into the input parser?  Well, the cool thing about the Windsor container is that it will automatically scan the constructors for classes it's aware of, and if that constructor has a dependency (i.e. the ISaleLineFactory interface) then the container will automatically instantiate an object for that interface and pass it through.


This makes managing dependencies and instantiation of classes so much simpler, and in large applications this is invaluable.


 


Now there is one other side effect to these changes.  By having loosely coupled objects writing unit tests is a lot easier now, and they are genuine unit tests, in other words they test one thing and one thing only.  Previously I would have had an input parser test as follows:

        [Test]
public void ParseACorrectLine()
{
saleLine = InputParser.ProcessInput("1 book at 12.49");
Assert.IsNotNull(saleLine);
Assert.AreEqual("book",saleLine.ProductName);
Assert.AreEqual(1, saleLine.Quantity);
Assert.AreEqual(12.49m, saleLine.Price);
}

 


But this is effectively an integration test.  It's testing that the parser and the saleline classes are both working and working together properly.  Not an ideal situation.


But with a loosely coupled design I can now use mocking and write a test as follows:

        [TestMethod]
public void ParseACorrectLine()
{
mocksRepository = new MockRepository();
saleLineFactory = mocksRepository.DynamicMock<ISaleLineFactory>();
sale = mocksRepository.DynamicMock<ISale>();
parser = new InputParser(display, saleLineFactory);
Expect.Call(saleLineFactory.GetNewSaleLine(1,"book",12.49m)).Return(new SaleLine(1,"book",12.49m));
mocksRepository.ReplayAll();
parser.ProcessInput("1 book at 12.49", sale);
mocksRepository.VerifyAll();
}

Now my test is checking that the interactions between the InputParser and the other classes are correct, not that the other classes are functioning correctly as well (which is what the first test did).  Note that the return of a new SaleLine object is just there to provide a return value for the call to the saleline factory.  Not having it would cause the test to fail.


 


Hopefully this is enough to get you started on improving that crappy code you have to work with every day and making it a more loosely coupled application, and as always I'd love some feedback :-)

2 comments:

  1. Hi Richard,

    I really enjoyed this entry it was very interesting - I love all factory stuff I find interfaces useful wherever I use them and normally give myself a pat on the back if I've found I've saved myself from writing even more code.

    However, I think the IoC stuff comes with a learning curve. The first example at the top of the entry is easily readable and I can tell exactly what is going on, fair enough you could throw in a few interfaces and abstract classes here and there to improve.

    The example app. at the bottom of the entry requires anyone maintaining that code to refer to the configuration file and then find the classes referenced in the config to really have an idea of what is going on.

    Would you agree that there's a trade off somewhere between solid loosely coupled easily extensible code and code that is easier to understand and maintain by the typical programmer?

    ReplyDelete
  2. And here was I worried that the post was long enough already, and the first comment say more info would have been better :-) Point taken.

    Re the tradeoff, I'd actually disagree. If you program to interfaces then you'll get to a point where you only care that you are using those interfaces correctly. Wether the concrete classes that implement the interfaces do their job properly is only a concern if you find a bug. That's when you fall back to your unit tests to see what you've missed, and the config file to have a look at what class implements the interface.

    Of course, there's an assumption that the interfaces are well documented (which they aren't in my sample code!).

    ReplyDelete