Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Dynamic Generic Factory

0.00/5 (No votes)
3 Sep 2015 1  
Dynamic, one class fits all factory with no need for specific implementations.

Background

Sitting in the dark feeding a new baby at 3am gives a person lots of time to ponder. So it was that I was reflecting on the factory tip I had posted a few weeks ago: http://www.codeproject.com/Tips/1020264/Factory-Pattern-for-Derived-Classes.

I wondered if a basic generic factory model could be designed in such a way as to do away with the need for specific type implementations completely. One factory to rule them all...

Using the code

The factory is a singleton with a generic type property (the interface you are generating in your concrete factory) and a single public static Create method.

Objects created by the factory are required to implement the IFactoryObject<T> interface. this defines two methods. FactoryId, which provides the key for the factory type lookup dictionary,and FactoryCreate, which returns a new T object.

public interface IFactoryObject<T>
{
    Enum FactoryId { get; }
    T FactoryCreate(object[] args);
}

The singleton Factory class is created as you might expect, but instead of an Instance property, the private instance is verified and instantiated in the Create method. When first called the singleton is initialized, reflecting all types derived from T and using their FactoryId property to populate a lookup dictionary.

public class Factory<T> where T : IFactoryObject<T>
{
    private static Factory<T> _instance;
    private Dictionary<Enum, IFactoryObject<T>> _lookup = new Dictionary<Enum, IFactoryObject<T>>();

    private Factory() { }

    public static T Create(Enum id, params object[] args)
    {
        if (_instance == null) CreateInstance();
        return _instance.CreateProduct(id, args);
    }

    private static void CreateInstance()
    {
        _instance = new Factory<T>();
        _instance.Initialize();
    }

    private void Initialize()
    {
        Type t = typeof(T);
        if (!t.IsInterface) return;

        var assembly = Assembly.GetExecutingAssembly();
        foreach (var type in assembly.GetTypes())
        {
            if (!type.IsClass || !t.IsAssignableFrom(type)) continue;
            Register(type);
        }
    }

    private void Register(Type type)
    {
        var inst = System.Activator.CreateInstance(type) as IFactoryObject<T>;
        _lookup.Add(inst.FactoryId, inst);
    }

Once initialized, the Create method will lookup your type and call IFactoryObject<T>.FactoryCreate to instantiate and return a new T object for you. 

public static T Create(Enum id, params object[] args)
{
    if (_instance == null) CreateInstance();
    return _instance.CreateProduct(id, args);
}

private T CreateProduct(Enum id, object[] args)
{
    return _lookup[id].FactoryCreate(args);
}

Factory objects are easy to create and there is no need to maintain any kind of lookup for your factory or type. 

public interface IPerson : IFactoryObject<IPerson>
{
    string PersonMethod1();
    string PersonMethod2();
}

 

public class PersonA : IPerson
{
    public Enum FactoryId { get { return PersonType.PersonTypeA; } }

    public virtual IPerson FactoryCreate(object[] args)
    {
        return new PersonA();
    }

    public string PersonMethod1()
    {
        return "Person A Method 1";
    }

    public string PersonMethod2()
    {
        return "Person A Method 2";
    }
}

Better still, there is absolutely no need to create, define, set up or otherwise waste time with any individual factories. Using the Create method with an  IFactoryObject Type  will create a new one for you!

IPerson personA = Factory<IPerson>.Create(PersonType.PersonTypeA);

Points of Interest

I made a Factory that takes an Enum and returns an Interface. That doesn't mean you have to. With some thought about how best to minimize runtime errors, you could use anything from object to complex types as your identifier, so long as it can be used as a Dictionary Key property. You can also return any class deriving from IFactoryObject<T>.

I just like Enums and Interfaces.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here