Click here to Skip to main content
15,887,861 members
Articles / Programming Languages / C#
Tip/Trick

Dynamic Generic Factory

Rate me:
Please Sign up or sign in to vote.
4.39/5 (8 votes)
3 Sep 2015CPOL2 min read 12.2K   9   5
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionType safety Pin
Chris at M6-Sep-15 15:19
Chris at M6-Sep-15 15:19 
AnswerRe: Type safety Pin
AndyLock7-Sep-15 3:00
AndyLock7-Sep-15 3:00 
GeneralRe: Type safety Pin
Chris at M8-Sep-15 14:15
Chris at M8-Sep-15 14:15 
QuestionIs it really better then factory method PersonA.Create(IResource resouce)? Pin
SergioRykov4-Sep-15 2:33
SergioRykov4-Sep-15 2:33 
AnswerRe: Is it really better then factory method PersonA.Create(IResource resouce)? Pin
AndyLock5-Sep-15 3:37
AndyLock5-Sep-15 3:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.