Monday, 27 August 2012

The object-oriected TDD journey

When you start practicing TDD for real, one of the first problems you'll come across is dependency management. Chances are, that before you were TDDing, whenever you needed to use class A from class B, B would create it's own instance of A or A might be a singleton.

class B
{
    private A instanceA = new A();
}

You soon find that you want to mock A and the best way to get the mock of A into an instance of B (your SUT), is to pass it into the constructor. This is called Dependency Injection:

class B
{
    public B(A a)
    {
        instanceA = a;
    }

    private readonly A instanceA;
}

You also find that you don't want a dependency on A because creating an instance of the mock of A is going to call the constructor of A. So you solve that by having A implement an interface IA and pass IA to B instead.

class B
{
    public B(IA a)
    {
        instanceA = a;
    }

    private readonly IA instanceA;
}

Your next problem comes along when you actually want to create an instance of B. Say the constructor of A takes an instance of IC, you will need to do the following when you want a new B:

B instanceB = new B(new A(new C))

For a large application, this step will be a horror of nested news. So you will then learn about Inversion Of Control and this will help you manage your dependencies:

IocContainer container = new IocContainer();
container.Register<B>();
container.Register<IA>().ImplementedBy<A>();
container.Register<IC>().ImplementedBy<C>();
B instanceB = container.Resolve<B>();

Here the IOC framework is responsible for figuring out the dependencies of B and creating the required instances recursively.

Now you find that you're making a mess becase your IOC framework is letting you create a dependency from any class in your system to any other. You now pay attention to group your classes into modules with small, well-defined interfaces between them.

When working on a piece of code you will try and break it down into small coherent modules that look like stand-alone libraries. They will ideally have a very small set of public interfaces. The IOC registration will be done by the module itself and classes external to the module should only use the public interfaces. This prevents a spaghetti of inter-class dependencies.

No comments: