Creating a simple factory for objects with a fluent configuration API

When I recently came to writing a new component for a system I thought, "I love using fluent configuration in other libraries so I'm going to write this with a fluent configuration API!". Of course, as with most projects, I made a lot of mistakes and had to keep going back and rewriting it from the ground up.

I also wasn't able to find a lot of examples/discussion on creating fluent APIs for configuration so I decided to write a simple example that isn't really useful in itself but is useful as a learning exercise (at least it was for me!)

I decided to put the full source on Github rather than uploading a zip as I find downloading examples as a zip annoying!

The whole idea for the example was to have a simple way of creating a factory object that is able to create objects of a certain type according to some configured rules. To make life easier, I added a constraint to only allow objects with a parameterless constructor.

Or to put it another way:

public interface IFactory<out T> where T : class, new()
{
    T Build();
}

In order to actually use an IFactory, we need a way of building them. To keep the IFactory interface small, I wanted a separate class responsible for configuring a factory and then creating instances based on that configuration:

public interface IFactoryBuilder<T> where T : class, new()
{
    IFactoryBuilder<T> ForMember<TProp>(
        Expression<Func<T, TProp>> accessor,
        Func<T, TProp> result);

    IFactory<T> Compile();
}

The signature may look scary, but all I'm saying is that I want people to specify access to a member and a function that returns a value for that member like:

IFactoryBuilderInstance<T>.ForMember(t => t.SomeStringProp, t => "Constant");

So what do the implementations of these interfaces look like?

public class FactoryBuilder<T> : IFactoryBuilder<T> where T : class, new()
{
    private readonly IDictionary<string, Delegate> _evaluators =
        new Dictionary<string, Delegate>();
      
    public IFactoryBuilder<T> ForMember<TProp>(
            Expression<Func<T, TProp>> accessor,
            Func<T, TProp> result)
    {
        _evaluators[ExpressionHelper.GetPropertyName(accessor)] = result;
        return this;
    }

    public IFactory<T> Compile()
    {
        return new SimpleFactory<T>(_evaluators);
    }
}

The factory builder keeps track of all of the member mappings that have been added by adding them/updating a dictionary which is then passed to the SimpleFactory constructor when you call compile. Returning `this` from the ForMember method allows you to chain multiple calls.

class SimpleFactory<T> : IFactory<T> where T : class, new()
{
    private IDictionary<string, Delegate> _evaluators;

    public SimpleFactory(IDictionary<string, Delegate> _evaluators)
    {
        this._evaluators = _evaluators;
    }

    public T Build()
    {
        var result = new T();
        var type = typeof (T);

        var props = type.GetProperties();

        foreach (var e in _evaluators)
        {
            var propInfo = props.FirstOrDefault(p => p.Name == e.Key);
            var value = e.Value.DynamicInvoke(result);
            if (propInfo != null) propInfo.SetValue(result, value);
        }
        return result;
    }
}

The implementation of these isn't really that important - it's just a very naive way of creating an object with reflection.

So what does using it look like?

public class Program
{
    static void Main(string[] args)
    {
        IFactoryBuilder<Test> builder = new FactoryBuilder<Test>();
        var rand = new Random();
        
        // The result gets built in the order that the specifications were added.
        // This means that it's fine for later specs to use already specced values.
        var testFactory = builder.ForMember(t => t.A, t => "A is always the same")
            .ForMember(t => t.B, t => rand.Next(0, 100))
            .ForMember(t => t.C, t => t.B.HasValue && t.B > 50)
            .ForMember(t => t.D, t => "but B is random and C is based on B!")
            .Compile();

        Console.WriteLine(testFactory.Build().ToString());
        Console.WriteLine(testFactory.Build().ToString());
        Console.WriteLine(testFactory.Build().ToString());
        Console.WriteLine(testFactory.Build().ToString());
        Console.WriteLine("Press any key to continue...");
        Console.ReadKey();
    }
}

Which produces:


Obviously this factory leaves a bit to be desired - recursion will kill it, if you don't map a member there are no default actions but hopefully it's a useful example on one way to create a fluent configuration API.

Comments

Popular posts from this blog

Trimming strings in action parameters in ASP.Net Web API

Full text search in Entity Framework 6 using command interception

Composing Expressions in C#