Factory Pattern without a Switch or If/Then
Asked Answered
F

4

31

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);
Fania answered 23/11, 2015 at 18:40 Comment(6)
You could take a look at the Abstract Factory Pattern and use dependancy injection to pass along the right factory for the job.Quarterdeck
I would recommend something like Ninject or AutofacCoolth
This is a classic case of dependency injection. The most basic use of any IoC container (Unity, Ninject, etc...) is precisely using it as a glorified Factory.Crutchfield
@Adimeus ... I would love to see an example using Dependency Injection and IoC. Could I ask you to provide one?Fania
@Tanuji ... I would love to see an example using Dependency Injection and IoC. Could I ask you to provide one?Fania
@CaseyCrookston - I updated my answer so that it utilizes interfaces based on your request in one of the other comments.Aylward
A
14

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();
    }
}
Aylward answered 23/11, 2015 at 19:7 Comment(13)
I like the simplicity of this a lot. But when I try it out, I get a blank console. Let me digest this a bit.Fania
There is no code there to output the contents of positions. Simply add a foreach(Position p in positions)...Aylward
How are you mapping the id numbers to new instances? I see you could pull an instance from positions by index, roughly corresponding to the ids, but what if you need multiple separate instances of a `Manager'?Williford
@BradleyUffner - The id numbers are not required. If you need more than 1 instance of a manager simply call Factory.Create<Manager>() more than 1 time.Aylward
If i could just call Factory.Create<Manager>() why wouldn't I just call new Manager() instead? The goal of the factory pattern is to reduce coupling between components.Williford
@Bradley Uffner: You asked, "why wouldn't I just call new Manager() instead?" I may be overlooking something here, but calling new Manager() is what the example in the OP does. The example is, of course, overly simplistic. In a real-world scenario, the abstract class (or interface) Position would be more involved. Using the answer provided by Clay here, I could do this instead: Position position0 = Factory.Create<Manager>(); And now I'm creating an instance of Position that is unique to manager.Fania
He could just call new Manager(), but from the example the poster seemed to be interested in basic creation patterns. If I was doing this myself (I actually did code it this way), I would add an interface IPosition with the single Title getter and have Position implement it explicitly. Then list becomes a List<IPosition> and the only way to access the Title getter is with an IPositon (an instance of Position and its subclasses won't have access because of the explicit implementation.Aylward
Where does the code cover the "default" case? E.g. in the example if you pass 3 you get a Programmer. In you example that case is not considered right @Clay Ver Valen?Fleeting
@Fleeting - The for loop is just there so the OP could see some output. But to your question, there is no "default" position in this creation pattern (not sure where "3" comes from as the valid index values are 0 - 2 inclusive). What would a "default" employee be?Aylward
@ClayVerValen According to @Casey Crookston 's example it is a ProgrammerFleeting
@Fleeting - The OP's question was on how to implement the Factory pattern without the use of a switch or if and clearly my answer suited their needs. If you have a need for a Factory pattern that includes a default case, simply add a parameterless Create function to the example Factory class that returns an instance of whichever class you would like to be your default (e.g. public static IPosition Create() { return new Programmer(); }Aylward
@ClayVerValen Imho that would violate the dependencies. The generic class Factory would then know one concrete implementation of IPosition. But anyway, the answer seems to meet the OP's requirements although it is - from a functionality point of view - different from his sample code.Fleeting
Without telling the Factory which implementation of IPosition to create (via a parameter to the Create<T> function) or having a parameterless Create that returns a hard coded default (which is what the OP's code did), how would you propose informing the Factory class of what the default should be?Aylward
F
8

You could make use of custom attributes and reflection.

[PositionType(1)]
class Manager : Position
{
    public override string Title
    {
        get
        { return "Manager"; }
    }
}

[PositionType(2)]
class Clerk : Position
{
    public override string Title
    {
        get
        { return "Clerk"; }
    }
}

In your factory you could then get all classes that inherit from Position and find the one that has the PositionType attribute with the correct value.

static class Factory
{
    public static Position Get(int id)
    {
        var types = typeof(Position).Assembly.GetTypes()
            .Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(Position)))
            .ToList();

        Position position = null;
        foreach(var type in types)
        {
           type.GetCustomAttributes<PositionTypeAttribute>();

           if(type.PositionId == id)
           {
               position = Activator.CreateInstance(type) as Position;
               break;
           }
        }

        if(position == null)
        {
            var message = $"Could not find a Position to create for id {id}.";
            throw new NotSupportedException(message);
        }

        return position;
    }
}
Fascinator answered 23/11, 2015 at 18:44 Comment(9)
Ok, I can see this working. But reflection seems like overkill? I like the suggestion above by Adimeus of using the Abstract Factory Pattern and use dependancy injection to pass along the right factory for the job. I'm just not sure yet how to do this.Fania
Even using that pattern you STILL have to implement the factory somehow. Even if you use something premade you that premade thing is going to use reflection internally.Williford
@CaseyCrookston, I guess it depends on your definition of overkill. I just looked at the UML diagram and sample code for the abstract pattern factory implementation and it seems like you're going to end up with a lot more code that (arguably) is going to be harder to maintain.Fascinator
Oh I'm sure it's less code for this particular example. But this is a very simple example. If the classes involved got a lot more complex and numerous, I'm worried about the cost of reflecting over all of them. But still, this is a great solution for a simple class structure. I'm putting together a little library of examples, and I'll include this one. Thank you!Fania
We use an approach similar to this in our message processing. We have event handlers that can process many event types. They advertise what they can handle using an attribute. I think the "cost of reflecting" gets overblown. I've done bench marking and reflection really isn't as expensive as the "common wisdom" makes it out to be. Also, given that the number of types is basically fixed (you would need to deploy a new assembly to change it) you could cache all that information on system startup and never need to load it again until you restart (e.g. after a redeploy).Fascinator
Sure, the "cost of reflecting" is not that great, yet it isn't fast either. If used in some critical piece of code that gets called alot, it might affect performance. That said, I guess all DI libraries (if my suggestion is implemented, you'll need one) use some sort of reflection aswell (educated guess, can't support this with facts), thus, destroying the need for this argument.Quarterdeck
Reflection is not slow, if you cache the results and only use it once there is nothing about it. The problem with reflection more lies in the fact that you will only encounter runtime errors. @Adimeus when you compile stuff into delegates there is not much drawback in means of performance left to worry about.Easygoing
This is a GREAT comparison of the overhead involved in the different methods of instantiation. marccostello.com/newing-up-tWilliford
@CSharpie, you're absolutely right about the run-time errors. When dynamically creating stuff using types you definitely need more guard conditions in your code. The code I provided should be pretty resilient in that regard (mainly because it's only working with types that it discovered itself) but I've written some reflection-based code in the past that relied on strings stored in the database. That stuff can be super-fragile if you're not careful.Fascinator
W
3
public class PositionFactory
{
    private Dictionary<int, Type> _positions;

    public PositionFactory()
    {
        _positions = new Dictionary<int, Type>();
    }

    public void RegisterPosition<PositionType>(int id) where PositionType : Position
    {
        _positions.Add(id, typeof(PositionType));
    }

    public Position Get(int id)
    {
        return (Position) Activator.CreateInstance(_positions[id]);
    }
}

Used like this:

            var factory = new PositionFactory();
            factory.RegisterPosition<Manager>(0);
            factory.RegisterPosition<Clerk>(1);

            Position p = factory.Get(0); //Returns a new Manager instance
Williford answered 23/11, 2015 at 18:50 Comment(9)
Ok, thanks for this! I've been staring at it for a while to make sure I understand all that's going on. Question: Would this qualify as an Abstract Factory Pattern?Fania
No, this is just a simple example of a regular factory pattern. It could be used as part of an abstract factory though, if you had multiple implementations of PositionFactory. An abstract factory is just a factory of factories, so you could use a very similar pattern to this in order to make an abstract factory. In fact, you could take both answers posted so far as the 2 different implementations of PositionFactory, as they use almost identical interfaces, and have a different factory that returns a PositionFactory of either type.Williford
The OP and this example work in a different way. The OP predetermines a code for each PositionType, where this example allows any code to have any positionSolder
True Sten, but I don't really care. I'm just looking for the best way to create any instance of a position without having to list them all in the factory class.Fania
You would do well to add interfaces. Seeing the code without interfaces makes my eyes hurt.Crutchfield
I was trying to stick as closely as possible to the API the Op originally used in his example. If this were part of an actual project of mine there would be a LOT more code, including interfaces.Williford
Tanuki and/or Bradley, I'd love to see a rewrite of this simple little example (that I extracted from a tutorial) with proper use of interfaces!!Fania
I will update my answer with interfaces. This will also allow me to demonstrate how even though one Create<Manager>() it is still decoupled.Aylward
@BradleyUffner, I'm looking at your answer trying to figure out how to modify it so that it doesn't use the dictionary. How could I call public IPosition Get(), and pass in the type directly?Fania
C
-1

Why overcomplicate things? Here is one simple solution:

using System;
using System.Collections.Generic;

class Program
{
    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"; }
        }
    }

    class Factory
    {
        private List<IPosition> positions = new List<IPosition>();
        public Factory()
        {
            positions.Add(new Manager());
            positions.Add(new Clerk());
            positions.Add(new Programmer());
            positions.Add(new Programmer());
        }

        public IPosition GetPositions(int id)
        {
            return positions[id];
        }
    }

    static void Main(string[] args)
    {
        Factory factory = new Factory();

        for (int i = 0; i <= 2; i++)
        {
            var position = factory.GetPositions(i);
            Console.WriteLine("Where id = {0}, position = {1} ", i, position.Title);
        }
        Console.ReadLine();
    }
}

Here is how to do this without using factory class at all:

using System;
using System.Collections.Generic;

class Program
{
    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 void Main(string[] args)
    {
        List<IPosition> positions = new List<IPosition> { new Manager(), new Clerk(), new Programmer(), new Programmer() };

        for (int i = 0; i <= 2; i++)
        {
            var position = positions[i];
            Console.WriteLine("Where id = {0}, position = {1} ", i, position.Title);
        }
        Console.ReadLine();
    }
}
Crutchfield answered 23/11, 2015 at 19:23 Comment(7)
This requires modifying the factory class whenever you add a new position. It is also only ever capable of handing out one single instance of each position.Williford
Well, then you just need to move adding positions outside of Factory class. They do need to be added somewhere... In that case you don't even need a factory class. using a list will doCrutchfield
I wouldn't consider it a factory unless it is actually instantiating the thing it is returning. This is more like the Service Locator Pattern, only without the "Service"Williford
This is pretty close to the suggestion by Clay Ver Valen.Fania
The example in my OP is simplistic enough that a factory isn't needed. But my point in posting this is to learn the factory pattern so I can use it in more complex situations.Fania
this code isn't equivalent to the Op's original code. In the original, each call to Get returns a NEW instance. This example will always return the SAME instance of each type of Position.Williford
Fair enough. In that case the dictionary answer provided by Bradley is the one that I have seen used the most in similar situations.Crutchfield

© 2022 - 2024 — McMap. All rights reserved.