Factory Pattern without a Switch or If/Then

Casey Crookston picture Casey Crookston · Nov 23, 2015 · Viewed 16.3k times · Source

I'm looking for a simple example of how to implement a factory class, but without the use of a Switch or an If-Then statement. All the examples I can find use one. For example, how could one modify this simple example (below) so that the actual factory does not depend on the Switch? It seems to me that this example violates the Open/Close principle. I'd like to be able to add concrete classes ('Manager', 'Clerk', 'Programmer', etc) without having to modify the factory class.

Thanks!

class Program
{
    abstract class Position
    {
        public abstract string Title { get; }
    }

    class Manager : Position
    {
        public override string Title
        {
            get  { return "Manager"; }
        }
    }

    class Clerk : Position
    {
        public override string Title
        {
            get { return "Clerk"; }
        }
    }

    class Programmer : Position
    {
        public override string Title
        {
            get { return "Programmer"; }
        }
    }

    static class Factory
    {
        public static Position Get(int id)
        {
            switch (id)
            {
                case 0: return new Manager();
                case 1: return new Clerk();
                case 2: return new Programmer();
                default: return new Programmer();
            }
        }
    }

    static void Main(string[] args)
    {
        for (int i = 0; i <= 2; i++)
        {
            var position = Factory.Get(i);
            Console.WriteLine("Where id = {0}, position = {1} ", i, position.Title);
        }
        Console.ReadLine();
    }
}

UPDATE:

Wow! Thanks everyone! I have learned a ton. After revewing all the feedback, I blended a few of the answers and came up with this. I'd be open to further dialog about a better way to do this.

class Program
{

    public interface IPosition
    {
        string Title { get; }
    }

    class Manager : IPosition
    {
        public string Title
        {
            get { return "Manager"; }
        }
    }

    class Clerk : IPosition
    {
        public string Title
        {
            get { return "Clerk"; }
        }
    }

    class Programmer : IPosition
    {
        public string Title
        {
            get { return "Programmer"; }
        }
    }

static class PositionFactory
{
    public static T Create<T>() where T : IPosition, new()
    {
        return new T();
    }
}


static void Main(string[] args)
    {

        IPosition position0 = PositionFactory.Create<Manager>();
        Console.WriteLine("0: " + position0.Title);

        IPosition position1 = PositionFactory.Create<Clerk>();
        Console.WriteLine("1: " + position1.Title);

        IPosition position2 = PositionFactory.Create<Programmer>();
        Console.WriteLine("1: " + position2.Title);

        Console.ReadLine();
    }
}

Another Edit:

It's also possible to create an instance of the Interface using an unknown type:

static class PositionFactory
{
   public static IPosition Create(string positionName)
    {       
        Type type = Type.GetType(positionName);
        return (IPosition)Activator.CreateInstance(type);
    }
}

Which could then be called as follows:

IPosition position = PositionFactory.Create("Manager");
Console.WriteLine(position.Title);

Answer

Clay Ver Valen picture Clay Ver Valen · Nov 23, 2015

How about this (no Dictionary required and note that you will get an syntax error if your try to Create<Position>()):

EDIT - Updated to use an IPosition interface implemented explicitly. Only instances of IPosition can access the member functions (e.g. <implementation of Manager>.Title will not compile).

EDIT #2 Factory.Create should return an IPosition not T when using the interface properly.

using System;
using System.Collections.Generic;

class Program
{
    interface IPosition
    {
        string Title { get; }
        bool RequestVacation();
    }

    class Manager : IPosition
    {
         string IPosition.Title
        {
            get { return "Manager"; }
        }

        bool IPosition.RequestVacation()
        {
            return true;
        }
    }

    class Clerk : IPosition
    {
        int m_VacationDaysRemaining = 1;

        string IPosition.Title
        {
            get { return "Clerk"; }
        }

        bool IPosition.RequestVacation()
        {
            if (m_VacationDaysRemaining <= 0)
            {
                return false;
            }
            else
            {
                m_VacationDaysRemaining--;
                return true;
            }
        }
    }

    class Programmer : IPosition
    {
        string IPosition.Title
        {
            get { return "Programmer"; }
        }

        bool IPosition.RequestVacation()
        {
            return false;
        }
    }

    static class Factory
    {
        public static IPosition Create<T>() where T : IPosition, new ()
        {
            return new T();
        }
    }

    static void Main(string[] args)
    {
        List<IPosition> positions = new List<IPosition>(3);
        positions.Add(Factory.Create<Manager>());
        positions.Add(Factory.Create<Clerk>());
        positions.Add(Factory.Create<Programmer>());

        foreach (IPosition p in positions) { Console.WriteLine(p.Title);  }
        Console.WriteLine();

        Random rnd = new Random(0);
        for (int i = 0; i < 10; i++)
        {
            int index = rnd.Next(3);
            Console.WriteLine("Title: {0}, Request Granted: {1}", positions[index].Title, positions[index].RequestVacation());
        }

        Console.ReadLine();
    }
}