About me

Michael L Perry

Improving Enterprises

Principal Consultant

@michaellperry

User login

The case for IoC containers

An IoC container helps make code more maintainable. It prevents changes to one part of a system from breaking other parts of the system. It allows developers to make those changes in isolation. And, in the case of the Q.E.D. IoC container, it encourages patterns that prove code correctness.

The Dependency Inversion Principle (DIP)
The Dependency Inversion Principle (DIP) is one of the five SOLID principles, authored by Robert C. Martin. It states that we should favor dependencies upon the abstract over dependencies upon the concrete. Dependencies upon abstract concepts make code less rigid.

Let’s walk through an example that Uncle Bob often presents. A copy program reads from the keyboard and writes to the screen.

static void Main()
{
    Keyboard keyboard = new Keyboard();
    Screen screen = new Screen();
    while (true)
    {
        char c = keyboard.Read();
        screen.Write(c);
    }
}

The Main method depends upon the Keyboard and Screen concrete classes. It cannot read from or write to anything else. This code is rigid.

To make the code less rigid, we can invert the dependency.

static void Main()
{
    ICharacterSource keyboard = new Keyboard();
    ICharacterTarget screen = new Screen();
    Copy(keyboard, screen);
}

private static void Copy(ICharacterSource source, ICharacterTarget target)
{
    while (true)
    {
        char c = source.Read();
        target.Write(c);
    }
}

The Copy method depends upon abstract types (ICharacterSource and ICharacterTarget), not on concrete types. The Main method decides which concrete types to use. Now Copy is less rigid, because it can be called with anything that implements the right interface.

Dependency injection

Dependency injection is a way of achieving dependency inversion. It means that you pass dependent objects into a class. This can be done either through the constructor or through a property. Here’s an example of constructor injection:

static void Main()
{
    ICharacterSource keyboard = new Keyboard();
    ICharacterTarget screen = new Screen();
    Copier copier = new Copier(keyboard, screen);
    copier.Copy();
}

class Copier
{
    private ICharacterSource _source;
    private ICharacterTarget _target;

    public Copier(ICharacterSource source, ICharacterTarget target)
    {
        _source = source;
        _target = target;
    }

    public void Copy()
    {
        while (true)
        {
            char c = _source.Read();
            _target.Write(c);
        }
    }
}

Q.E.D. prefers constructor injection over property injection because the constructor proves immutability.

Inversion of control

Inversion of control (IoC) containers facilitate dependency injection. Whereas manual dependency injection moves the concrete dependencies up the chain, an inversion of control container moves the dependencies outside of the source code entirely.

If we were using an IoC container, the Main method would look like this:

static void Main()
{
    Copier copier = new Container().Resolve<Copier>();
    copier.Copy();
}

The IoC container would know that the Copier needs an ICharacterSource and an ICharacterTarget. Furthermore, it would know that Keyboard and Screen provide those services. It could therefore resolve that dependency with no additional information.

Because dependencies are inverted, each class is less dependent upon other classes. We can change the behavior of one class simply by changing its dependencies. Because the IoC container is managing dependencies on our behalf, we can make changes in isolation. There is no need to change the caller when a class takes on a new dependency. And because we favor constructor injection, we can prove things like immutability.