Object Creation Architecture

AutoFixture's kernel creates objects through a set of interfaces and an engine that mainly uses the Chain of Responsibility pattern. From a high-level perspective, we request a specimen from the kernel, and it returns an instance if any participant from the Chain of Responsibility can satisfy that request.

Requests

To request a specimen we invoke the Create method from the ISpecimenBuilder interface:

public interface ISpecimenBuilder
{
    object Create(object request, ISpecimenContext context);
}

So what is a request? Technically, it can be any object, but in the typical scenario it's a Type instance. When we invoke the CreateAnonymous<T> or CreateMany<T> extension methods, they eventually end up invoking the Create method with typeof(T) as the request. However, it should be pointed out that although this is the typical scenario, it may result in lots of subsidiary requests for other instances, such as PropertyInfo, ParameterInfo or completely custom types. As long as at least one ISpecimenBuilder can handle the request, the request is considered valid.

Handling Requests

The AutoFixture kernel is built around a chain of ISpecimenBuilder instances, typically encapsulated in a CompositeSpecimenBuilder. When we invoke the Create method, a CompositeSpecimenBuilder will invoke the Create method of all its contained builders until one of them provides a specimen. At this point the request is considered to be satisfied, and the rest of the builders are ignored. The created specimen is returned to the caller.

It is important to notice that null is considered a valid specimen, so to signal that it cannot satisfy a request, a Specimen Builder must return an instance of the NoSpecim signal type:

return new NoSpecimen(request);
Any other returned value (including null) is considered a valid specimen, which stops the Chain of Responsibility and returns the specimen.

The Specimen Context

When requesting a specimen we must also provide an ISpecimenContext instance. This is the context in which the request is handled:

public interface ISpecimenContext
{
    object Resolve(object request);
}
The Resolve method simply provides a mechanism where a Specimen Builder can create subsidiary specimens to satisfy a request. As an example, the ConstructorInvoker Specimen Builder satisfies requests for types by using Reflection to invoke a public constructor on a Type. If that constructor requires parameters, ConstructorInvoker requests from the context specimens based on the corresponding ParameterInfo instances.

Each time the context.Resolve is invoked, the SpecimenBuilder Chain of Responsibility is recursively invoked with the new request.

One of the concepts that most extenders seem to have a hard time to grasp is the concept of a Relay. This is an ISpecimenBuilder that handles a request by issuing a new (different) request to the context and then wraps the result of that operation in a new specimen. One example is the ArrayRelay, which handles requests for arrays by issuing a new request for multiple specimens of the array type and returning the sequence as an array. If you ever feel the desire to use Activator.CreateInstance to create specimens you should create a relay instead.

Extending the kernel

To extend the kernel, the ISpecimenBuilder is the main interface to implement. Since the first specimen created by a Specimen Builder is the specimen returned, the kernel can be customized by inserting specialized ISpecimenBuilder implementations early in the Chain of Responsibility.

Last edited Apr 26, 2011 at 7:41 AM by ploeh, version 7

Comments

No comments yet.