Search code examples
c#genericsdesign-patternsfactory-patternopen-closed-principle

How to load Func<T> from configuration file - Factory pattern - Open Closed principal


Below is my factory class:

public class BulkFactory<T>
{
    private BulkFactory() { }

    static readonly Dictionary<string, Func<T>> _dict
         = new Dictionary<string, Func<T>>();

    public static T Create(string id)
    {
        Func<T> constructor = null;
        if (_dict.TryGetValue(id, out constructor))
            return constructor();

        throw new ArgumentException("No type registered for this id");
    }

    public static void Register(string id, Func<T> ctor)
    {
        _dict.Add(id, ctor);
    }
}

And this is how I am registering various bulk job to this factory:

BulkFactory<IBulk>.Register("LCOUPD", () => new BAT_LCOUPD<BulkLCOUpdateRecord, BAT_LCOUPD_LOG>(bulkJobCode));
BulkFactory<IBulk>.Register("STBILL", () => new BAT_STBILL<BulkStartStopBillUpdateRecord, BAT_STBILL_LOG>(bulkJobCode));
BulkFactory<IBulk>.Register("PLANCH", () => new BAT_PLANCH<BulkPlanChangeUpdateRecord, BAT_PLANCH_LOG>(bulkJobCode));
BulkFactory<IBulk>.Register("CSCORE", () => new BAT_CSCORE<BulkCSCOREUpdateRecord, BAT_CSCORE_LOG>(bulkJobCode));
BulkFactory<IBulk>.Register("CUSTAQ", () => new BAT_CUSTAQ<CustomerAcquisitionTemplate, BAT_CUSTAQ_LOG>(bulkJobCode));

In order to follow Open Closed principal I want to store all these register entries into configuration file and wanted to load it back from configuration. So that whenever I add a new bulk job I need not to modify above lines of code.

Please suggest how can I achieve this.


Solution

  • Assuming you have a FactoryMethod array:

    public sealed class FactoryMethod {
        public string Name { get; set; }
        public string Type { get; set; }
    }
    

    Which you somehow read from your configuration file where Name is class name you want to register (LCOUPD, STBILL and so on) and Type is in the format required by Type.GetType() for generic types:

    TClass`2[TGenericType1, TGenericType2].

    For example (assuming a fictional namespace Ns):

    Ns.BAT_CUSTAQ`2[Ns.CustomerAcquisitionTemplate, Ns.BAT_CUSTAQ_LOG]

    Then you may write this code:

    foreach (var factoryMethod in factoryMethods) {
        BulkFactory<IBulk>.Register(factoryMethod.Name,
            (IBulk)Activator.CreateInstance(Type.GetType(factoryMethod.Type), bulkJobCode));
    }
    

    Note that I don't know exact type of your classes and prototype of your functions then you may need a cast here or there.