Geeklog
There's no place like 127.0.0.1 - ramblings about programming

Advanced Object Builder - Factory Methods

April 17, 2008 09:40 by The Geek

This a continuation from here and here.

I am going to write about how we can extend our object builder to include support for factory methods.  Now that we have custom config sections, it becomes easy to add this feature.  As factory methods are used for the creation of instances, all the following code will be added to InstanceManufacturer<T>.  The first thing that we need to do is to obtain the configuration section:

    private static FactoryMethodConfiguration _factoryConfig;

    static InstanceManufacturer()
    {
        _factoryConfig = ConfigurationManager.GetSection(
            FactoryMethodConfiguration.FactoryMethodConfigurationName)
               as FactoryMethodConfiguration;

        if (_factoryConfig == null)
        {
            throw new ApplicationException(Resources.CantLoadFactoryCfg);
        }
    }

Now that we have a reference to the configuration settings that pertain to the factory methods, we can determine whether we need to use a factory method or not:

    internal static T CreateInstance(params object[] args)
    {
        Type typeToCreate = typeof(T);

        //do we need to use a factory method?
        if (_factoryConfig.Methods.ContainsKey(typeToCreate.FullName))
        {
            return CreateWithFactory(typeToCreate, args);
        }
        else
        {
            return CreateWithActivator(typeToCreate, args);
        }
    }

As you can see, we have refactored the CreateInstance method so that it will call the appropriate method based on whether the type we want to create is listed in the methods collection of the factory configuration.  To see the full code contained within the CreateWithFactory method, download the source code.  The important parts are as follows:

First, we need to get a reference to the item contained within the configuration section.

    //get the factory info
    FactoryMethodItem factory =
        _factoryConfig.Methods.GetItemByKey(typeToCreate.FullName);

Now that we have a FactoryMethodItem, we can get the type of the factory class.  We do this by loading the assembly and getting the type from it.

    //get the type
    Type factoryType = Assembly.Load(factory.FactoryAssembly).GetType(factory.FactoryType);

As we now have the type of the factory, using reflection, we can find the factory method:

    //try to find a factory method
    MethodInfo method = factoryType.GetMethod(factory.FactoryMethod,
                                    BindingFlags.Public
                                    | BindingFlags.Static
                                    | BindingFlags.FlattenHierarchy);

All that is left to do is invoke the method and return the result:

    //invoke the method
    return (T)method.Invoke(null, null);

Again, a simple piece of a more complex puzzle!

To use the code is still simple:

    static void Main(string[] args)
    {
        Dog dog = Builder.Build<Dog>();
        Console.WriteLine("Legs: {0}, Talk: {1}.", dog.Legs, dog.Talk);

        Cat cat = Builder.Build<Cat>();
        Console.WriteLine("Legs: {0}, Talk: {1}.", cat.Legs, cat.Talk);

        Console.ReadLine();
    }

Download the source code to see how I pass parameters to the factory method.  You should be aware that this implementation is not going to provide great performance - depending on the application, you may need to cache types and assemblies.  Another issue is that any assemblies that are to be loaded must exist within the application's bin directory.

In my next post I will show you how we can add type substitution to the object builder.

Source code: ObjectBuilder_Factory.zip (96.45 kb)


Comments