Wednesday, July 6, 2011

Introduction to IoC Containers and Dependency Injection using StructureMap

In a previous article, I discussed the nature of the IoC principle as a separate entity from Dependency Injection. In this article I want to explain IoC Containers, and show how these can be used alongside Dependency Injection to create clean, testable, scaleable code, via decoupled architectures.

But before I explain what IoC Containers and DI are, let's talk about why you would want them.

Decoupled Architectures
In my IoC post, we looked at an example in which we handed over control to a GUI framework. I'm going to continue with that example here, and have included a cut-down version of the class in the codeblock below.

(I've renamed the class GuiFramework to GraphicUserInterface, because the original naming was only intended to highlight the differences between libraries and frameworks.)

public class BusinessLogic {

    private GraphicUserInterface gui = 
                    new GraphicUserInterface();

    public void Run(){
        gui.NamePrompt("Please enter your name");
        gui.AddNameChangedHandler(OnNameChanged);
    }
}

public class Program() {
    public static void Main() {
        BusinessLogic busLogic = new BusinessLogic();
        busLogic.Run();
    }
}

One of the main benefits of the IoC approach is that we have a separation of concerns. The GraphicUserInterface class handles all of the stuff to do with GUIs. The BusinessLogic class handles all of the stuff to do with business logic.

The business logic class manipulates the GUI class in order to implement business logic functionality, but it doesn't know anything about how, when or where the GUI does it's job. It simply uses the GUI's services and that's it.

So our architecture is almost decoupled. The only thing that still couples our architecture is that the business logic class directly instantiates an instance of the GraphicUserInterface class. With this setup we say that the BusinessLogic class depends on the GraphicUserInterface class, or that the GraphicUserInterface class is a dependency of the BusinessLogic class.

If we can properly decouple these components, then they become like 'plugins':

  • entirely independent of each other
  • entirely interchangeable with other components that provide the same or similar services
  • but still entirely inter-operable with each other - any plugged in component will work without adapting or making config changes to other components in the system - they will just 'plug and play'

Why That's Good
If you've worked with enterprise-level applications, or any application with more than a trivial number of components, it will be clear why this is positive. Here are a few important reasons:

  • Requirements changes - It is practically inevitable during any project that requirements will change. Decoupled architectures allow you to replace components in an existing system without having to adapt and recompile other components to match.
  • Unit testing - A decoupled component can be tested in isolation from it's dependencies, by mocking the dependencies. In this way unit tests can be run continuously throughout the project - and it becomes easy to have a development process in which your software always in a testable state. This is a huge advantage because it provides certain guarantees and confidence throughout. It also paves the way for TDD, which i'll discuss in another post.
  • Separation of concerns - it enforces an architecture where responsibilities are meted out to components neatly. This is nicer to work with, but it also has the practical benefit that if you bring a new developer onto the project, they can start work very quickly, and with a minimum of knowledge about the system. For example, if they are working on a data-access abstraction they need not ever have seen the UI - the responsibilities that this developer has will end at the boundary of the data-access abstraction, or whatever other component they are working on.

So let's see what that really means, by decoupling the example code, step by step.

Step 1: Use an Interface
Instead of referring to the implementation class directly by name, we'll use an interface. I've commented out the old declaration and added a new one.

public class BusinessLogic {

    //private GraphicUserInterface gui = new
    //                GraphicUserInterface();
    private IUserInterface userInterface;

    public void Run(){
        gui.NamePrompt("Please enter your name");
        gui.AddNameChangedHandler(OnNameChanged);
    }
}

public class Program() {
    public static void Main() {
        BusinessLogic busLogic = new BusinessLogic();
        busLogic.Run();
    }
}

First, notice the rename. The interface is called IUserInterface - not IGraphicUserInterface. This is important, because our business logic class doesn't need to know what kind of implementation it is dealing with. All it needs is the services of some kind of user interface, in order to prompt and accept a user's name. Exactly what kind of UI is unimportant - it could be a GUI or a web form or a speech recognition component - the BusinessLogic class doesn't care.

All the BusinessLogic class wants to know is that the class is capable of providing the services it requires. Implementation classes can indicate this capability by implementing the relevant interface - in this case IUserInterface.

Second, notice that we are not instantiating our new private userInterface member, only declaring it. Ofcourse if our application is going to work, the member will need to be instantiated - but how?

Step 2: Use Dependency Injection
We 'inject' a pre-instantiated object into our class:

public class BusinessLogic {

    private IUserInterface userInterface;

    public void BusinessLogic(IUserInterface aUserInterface){
        this.userInterface = aUserInterface;
    }

    public void Run(){
        gui.NamePrompt("Please enter your name");
        gui.AddNameChangedHandler(OnNameChanged);
    }
}

public class Program() {
    public static void Main() {
        BusinessLogic busLogic = new BusinessLogic();
        busLogic.Run();
    }
}

The approach used above is called Constructor Injection. Alternatively you could use Setter Injection, which is where you inject a dependency via a method (a setter method). In either case, the dependency is instantiated outside of the dependent class and injected in.

We'll use Constructor Injection in this example, and you'll see why a little later on.

Step 3: Set up an IoC Container
So where is the object instantiated? We need an IoC container.

These 'contain' the mappings required to wire up our dependencies. You'll hear them called DI containers too, but it means the same thing (there's a brief discussion on terminology at the bottom of this article).

I'm going to use StructureMap as an example because it's easy to use and has an intuitive syntax. First, we create a Bootstrapper. This becomes the single location within our program where concrete types are mapped to interfaces:

using StructureMap;

public static class Bootstrapper
{
    public static void Bootstrap()
    {
        ObjectFactory.Initialize(x =>
        {
            x.For<IUserInterface>().Use<GraphicUserInterface>();
            x.For<IBusinessLogic>().Use<BusinessLogic>();
        });
    }
}

StructureMap is one of a number of IoC Containers for .NET. Regardless of which container you use, you would expect to see all mappings declared inside a Bootstrapper, and the Bootstrapper to be created during initialisation (Global.asax for web applications, or the main routine for a desktop application).

StructureMap's Object Factory
The ObjectFactory mentioned in the codeblock above is a static wrapper for the StructureMap container. Whenever you use the ObjectFactory static class anywhere in your code, you are always referencing the same singleton container (it is possible to use more than one container object but this is not standard).

Once you have bootstrapped your mappings, you can use the static ObjectFactory reference anywhere in your code to instantiate an object by describing the service you require (passing in an interface name):

public class Program {
    public static void Main()
    {
        Bootstrapper.Bootstrap();
        var userInterface = 
            ObjectFactory.GetInstance<IUserInterface>();
    }
}

In the above example, the implicitly-typed var userInterface will now hold an instance of the GraphicUserInterface class.

Auto-Wiring
You might think at this stage, great - now we can just create a BusinessLogic object and send the userInterface object as an argument. But it's simpler than that, and this is one good reason to use Constructor Injection for your dependencies.

If you decouple all of your components, including (in this case) the BusinessLogic class, then StructureMap will figure out your dependencies for you using Auto-Wiring:

public class Program {

    public static void Main()
    {
        Bootstrapper.Bootstrap();

        //var userInterface = 
        //  ObjectFactory.GetInstance<IUserInterface>();
        var businessLogic = 
            ObjectFactory.GetInstance<IBusinessLogic>();

        businessLogic.Run();
    }
}

Notice that I have commented out the top line. Auto-wiring basically means that unless you tell the bootstrapper differently, any time you instantiate an object with dependencies StructureMap will create an instance of the dependency and inject it for you.

More With StructureMap
There's a lot more you can do with StructureMap, or any IoC Container. For example, you can wire up a singleton so that all dependencies resolve to a single instance:

x => x.For<IFoo>().Singleton().Use<Foo>()

You can use 'open generics', so that you can decouple your genericised classes. For example you might have a class Repository which could handle Book objects or Magazine objects, i.e. Repository<Book> or Repository<Magazine>. Using this syntax, you can declare mappings that are generics-agnostic:

x => x.For(typeof (IRepository<>)).Use(typeof (Repository<>))

You can specify an instance to use for particular constructor arguments. For example, if your class Foo is constructed with a string called argName:

x.For<IFoo>().Use<Foo>().Ctor<string>("argName").Is("arg");
//or
x.For<IFoo>().Use<Foo>().Ctor<string>("argName").
                           .EqualToAppSetting("app-setting-id");

This is ofcourse a whirlwind tour. Here are a few recommended links if you want to run off and figure out StructureMap now:

When To Use Which Approach
There will be times when Constructor Injection is not appropriate, and you need fine-grained control of object instantiation during program execution. In these circumstances you can use the ObjectFactory object as we did in the examples earlier:

var newFoo = ObjectFactory.GetInstance<IFoo>()

This gives you the same type of control over when things are instantiated as you get with ordinary coupled code. But it should be clear that the easiest approach whenever possible is to use Constructor Injection and let Auto-Wiring do the work for you. When this isn't possible move to Setter Injection, and when that isn't possible, use ObjectFactory to create an instance.

Decoupling Your Container
Ofcourse, ObjectFactory is a class defined within the StructureMap namespace. If you pepper it around your code (i.e. using GetInstance), then you are coupling your code to StructureMap. Some people prefer to ensure full decoupling from IoC Containers by housing Container references within custom object factories, and accessing those factories via an interface.

In another post I will discuss Autofac, a google code project and DI/IoC Container. Autofac, unlike StructureMap, ensures the container is fully decoupled by design. Instead of using a generic static Object Factory, Autofac allows you to create and inject custom object factories for use in your code.

The IoC/DI Name
You can see Martin Fowler here discussing the evolution of the name. Three different techniques / approaches have merged together to form a pattern, and so we have this composite name - IoC / DI / Container.

You can see what I mean if you consider that it's perfectly possible to use Dependency Injection without inverting control, or decoupling via interfaces:

public class BusinessLogic {

    private SomeLibrary someLibrary;

    public void BusinessLogic(SomeLibrary someLib){
        this.someLibrary = someLib;
    }
}

It's also possible to invert control without using DI, or to use containers without DI or IoC. However these approaches have grown to compliment each other and have become a mainstream approach to creating clean, testable, scaleable application architecures.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.