Saturday, December 28, 2013

Creating an IoC Container - Method Chaining Syntax

This is the forth part of a series on creating an IoC container. In the third part, the container assumed responsibility for an objects lifetime by managing creating it as a singleton or not. In this part, I will delve into making the registration process more readable through the use of method chaining. This is the approach that is currently being used by the open source IoC container called StructureMap.

Method Chaining

Method chaining allows a series of methods to be called one after another. The first method call will return an object that contains the next method to be called and so on. This allows a series of actions to be carried out and for the code to be represented in a very readable manner.

Making it More Readable

This is the container's current registration syntax:

container.register<IAbstractType, ContreteType>(isSingleton: true);

From that single line of code, it is not clear that the first generic argument is the requested type and that the second is the return type. If you had never used the container before, you could very easily get them mixed up. Through using method chaining, a more readable registration syntax can be created that will look like this:

container.For<IAbstractType>().Use<ConcreteType>().AsSingleton();

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public RegistrationExpression For<T>()
{
  var abstractType = typeof(T);

  var registration = new Registration()
  {
    AbstractType = abstractType
  };

  _registrations.Add(registration);

  var registrationExpression = new RegistrationExpression(registration);

  return registrationExpression;
}

The For method is where the requested type is specified. The first half of the implementation is the same as the Register method, however, the Registration object is only being populated with the AbstractType before it's added to the internal collection of registrations. The final act is to create a RegistrationExpression object with a reference to the Registration.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class RegistrationExpression
{
  private readonly Registration _registration;

  public RegistrationExpression(Registration registration)
  {
    _registration = registration;
  }

  public RegistrationExpression Use<T>()
    where T : class
  {
    _registration.ConcreteType = typeof(T);

    return this;
  }

  public RegistrationExpression AsSingleton()
  {
    _registration.IsSingleton = true;

    return this;
  }
}

The RegistrationExpression object is what enables the method chaining as it contains the other behaviors relevant to completing a registration. All the methods return this (in this case, the RegistrationExpression) and it is this that allows subsequent methods to be called in a chain. These methods update the Registration through the reference stored in the private _registration variable that was supplied when the object was created.

A complete project containing all the source code for the container and a set of unit tests can be found here.