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

Better factories in C#

Rate me:
Please Sign up or sign in to vote.
4.50/5 (15 votes)
17 Oct 2013CPOL1 min read 31.3K   28   17
This short article just shows the evolution of my implementation of the Factory pattern in C#.

Introduction

This short article just shows the evolution of my implementation of the Factory pattern in C#.

Since Factory pattern is so frequently used, this article describes better ways to implement it in C#.

I could go and show some UML to describe pattern but lets go straight to the code, people unfamiliar with this pattern can Google it.

Using the code

Below diagram shows example concrete class that our factory could build for us, they all implement same interface.

Image 1

I am going to use an enum ConcreteType as key to map concrete types, in your implementation you can use whatever better fits you.

Image 2

Factory Version 1

In this version work is done by a switch, it can be implemented even in Framework 1.0.

C#
using System;

namespace BetterFactories
{
    /// <summary>
    /// Verbose way
    /// </summary>
    public class FactoryV1
    {
        public IMyInterface Get(ConcreteType concreteType)
        {
            switch (concreteType)
            {
                case ConcreteType.Concrete1:
                    return new MyConcreteClass1();
                case ConcreteType.Concrete2:
                    return new MyConcreteClass2();
                case ConcreteType.Concrete3:
                    return new MyConcreteClass3();
                default:
                    throw new NotImplementedException();
            }
        }
    }
}

Factory Version 2

In this version we have a Dictionary were we keep mappings for our concrete types.

Since now we are using generics we need Framework 2.0 or above.

C#
using System;
using System.Collections.Generic;
namespace BetterFactories
{
    /// <summary>
    /// More stylish but slow and cast needed.
    /// </summary>
    public class FactoryV2
    {
        public FactoryV2()
        {
            _Mappings = new Dictionary<ConcreteType,Type>(3);
            _Mappings.Add(ConcreteType.Concrete1, typeof(MyConcreteClass1));
            _Mappings.Add(ConcreteType.Concrete2, typeof(MyConcreteClass2));
            _Mappings.Add(ConcreteType.Concrete3, typeof(MyConcreteClass3));
        }
        public IMyInterface Get(ConcreteType concreteType)
        {
            Type type;
            if(_Mappings.TryGetValue(concreteType, out type))
            {
                return Activator.CreateInstance(type) as IMyInterface;
            }
            else
                throw new NotImplementedException();
        }
        readonly Dictionary<ConcreteType, Type> _Mappings;
    }
} 

After doing a benchmark to Activator.CreateInstance class I realized there was a performance hit here.

Factory Version 3

Here we have a similar approach but now our Dictionary points to a Func<T> which improves performance and avoids casting.

Since this uses Func<T> we need Framework 3.5 or above.

C#
using System;
using System.Collections.Generic;
namespace BetterFactories
{
    /// <summary>
    /// Best way so far to have a factory, it performs well and types are compiler checked.
    /// </summary>
    public class FactoryV3
    {
        public FactoryV3()
        {
            _Mappings = new Dictionary<ConcreteType,Func<IMyInterface>>(3);
            _Mappings.Add(ConcreteType.Concrete1, () => new MyConcreteClass1());
            _Mappings.Add(ConcreteType.Concrete2, () => new MyConcreteClass2());
            _Mappings.Add(ConcreteType.Concrete3, () => new MyConcreteClass3());
        }
        public IMyInterface Get(ConcreteType concreteType)
        {
            Func<IMyInterface> func;
            if (_Mappings.TryGetValue(concreteType, out func))
            {
                return func();
            }
            else
                throw new NotImplementedException();
        }
        readonly Dictionary<ConcreteType, Func<IMyInterface>> _Mappings;
    }
} 

Points of Interest

After years of implementing this pattern it has changed several times but these three versions encapsulate the milestones.

If you have suggestions to improve it please bring up your comments.

Project attached contains all source code created in VS2008.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


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

Comments and Discussions

 
QuestionService Locator or Simple Dependecy Injecter Pin
Member 189736923-Oct-13 5:15
Member 189736923-Oct-13 5:15 
QuestionGreat Pin
Samuel Langlois21-Oct-13 8:32
Samuel Langlois21-Oct-13 8:32 
AnswerRe: Great Pin
arturomonriv21-Oct-13 8:42
arturomonriv21-Oct-13 8:42 
SuggestionFactory V3 for .NET 2.0 Pin
Olivier DALET18-Oct-13 0:35
Olivier DALET18-Oct-13 0:35 
Questionwhy recreate the wheel? Pin
Durrmeyer17-Oct-13 22:50
Durrmeyer17-Oct-13 22:50 
GeneralMy vote of 5 Pin
Paulo Zemek17-Oct-13 17:51
mvaPaulo Zemek17-Oct-13 17:51 
GeneralMy vote of 2 Pin
Florian Rosmann16-Oct-13 20:11
Florian Rosmann16-Oct-13 20:11 
GeneralRe: My vote of 2 Pin
ednrg18-Oct-13 5:22
ednrg18-Oct-13 5:22 
GeneralMy vote of 3 Pin
Klaus Luedenscheidt16-Oct-13 19:48
Klaus Luedenscheidt16-Oct-13 19:48 
GeneralConsider it as V4 PinPopular
Wonde Tadesse16-Oct-13 18:04
professionalWonde Tadesse16-Oct-13 18:04 
GeneralRe: Consider it as V4 Pin
Robert Rohde17-Oct-13 2:41
Robert Rohde17-Oct-13 2:41 
GeneralRe: Consider it as V4 Pin
arturomonriv17-Oct-13 3:15
arturomonriv17-Oct-13 3:15 
GeneralMy vote of 3 Pin
Paulo Zemek16-Oct-13 17:19
mvaPaulo Zemek16-Oct-13 17:19 
GeneralMany things to consider. Pin
Paulo Zemek16-Oct-13 16:11
mvaPaulo Zemek16-Oct-13 16:11 
I saw your implementations and I can see how it evolved, yet I think there are many things that could be better explained or improved.

You say that the Dictionary solution only works on .NET 2.0 and the solution that uses Func can only be used on .NET 3.5. Well, it is true that using exactly those solutions you will be bound to those versions of the framework. But in .NET 1.0/1.1 we could use a hashtable instead of a dictionary. And, we could always create a new delegate type, so we didn't need the generic Func type or, if using .NET 2.0 we could easily recreate such delegate (as we only need a single line of code to do it).

Now, focusing on other problems, I think it is better to use NotSupportedException instead of NotImplementedException, as the NotImplementedException is used to tell that a method wasn't implemented at all... well, you may argue that the method is not fully implemented if it doesn't support all the cases... but there's another problem.
Using an enum to tell which implementation to create is an anti-pattern. By doing it you will need to change the enum every time a new implementation as created and, worse, if the implementations are inside a closed library, someone else will not be able to add its own implementation even if he can implement the interface. That's why using something else (like a string) is a better solution. If we use a dictionary and a string key we can add new implementations at run-time (that is, a closed library can create user types). Of course this starts to go into the direction of an IoC container... but it is a much better practice.
GeneralRe: Many things to consider. Pin
arturomonriv16-Oct-13 17:06
arturomonriv16-Oct-13 17:06 
GeneralRe: Many things to consider. Pin
Paulo Zemek16-Oct-13 17:17
mvaPaulo Zemek16-Oct-13 17:17 
GeneralRe: Many things to consider. Pin
arturomonriv17-Oct-13 3:17
arturomonriv17-Oct-13 3:17 

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.