Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Factory Pattern, selecting by Property

I have a (growing) list of Data-Generators. The generator that I need is created by a factory class. The generators all implement a common Interface, which includes among other things a static string name.

What I would like to do: Call the factory.Create method with a string parameter for the above mentioned name. The create method finds the generator with this name and returns a new instance of said generator.

Bonus in my opinion of this way to do it: I only have to add new generator classes without having to edit the factory.

Question:

  1. Is this a good way to handle this problem?
  2. How can I find all generators? Reflection over every implementation of the interface/every member of the namespace (unique for the generators + their interface)?
  3. Is it correct to call this way of working a factory, or is this some different pattern?

In the end I would call the factory like this (simplified):

//Caller
public DataModel GetData2()
{
    var generator = new DataFactory().Create("Gen.2");
    return generator.GetData();
}

//Factory
public class DataFactory
{
    public AbstractDataGenerator Create(string type)
    {
        //Here the magic happens to find all implementations of IDataGenerator
        var allGenerators = GetImplementations();
        var generator = allGenerators.FirstOrDefault(f => f.name == type);
        if (generator != null)
            return (AbstractDataGenerator)Activator.CreateInstance(generator);
        else
            return null;
    }
}

//Interface
public abstract class AbstractDataGenerator
{
    public static string name;
    public abstract DataModel GetData();
}

//Data-Generators
public class DataGen1 : AbstractDataGenerator
{
    public static string name = "Gen.1";
    public DataModel GetData()
    {
        return new DataModel("1");
    }
}
public class DataGen2 : AbstractDataGenerator
{
    public static string name = "Gen.2";
    public DataModel GetData()
    {
        return new DataModel("2");
    }
}

Should the magic GetImplementations() in the factory be done via Reflection or somehow different? Should I use a completely different approach?

Since answers refer to IoC and DI: This project uses NInject already, so it would be available. Switched from interface to abstract class.

like image 599
MilConDoin Avatar asked Jul 05 '16 11:07

MilConDoin


People also ask

What is the Factory Method patterns explain with examples?

Example. The Factory Method defines an interface for creating objects, but lets subclasses decide which classes to instantiate. Injection molding presses demonstrate this pattern. Manufacturers of plastic toys process plastic molding powder, and inject the plastic into molds of the desired shapes.

Where you can apply Factory Pattern?

The factory design pattern is used when we have a superclass with multiple sub-classes and based on input, we need to return one of the sub-class. This pattern takes out the responsibility of the instantiation of a class from the client program to the factory class.

What is a Factory Method Pattern good for?

In class-based programming, the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created.


2 Answers

Is this a good way to handle this problem?

Having a factory to get an instance of the logic class you need by some key - I believe it is a good way. It is a pattern that I use a lot myself. About the way you have your key - I'd prefer to not have it as a static member (regardless to the fact that interfaces can't have static members) but just as a property and to add a base class to the IDataGenerator. That base class will have a constructor that will get the name - That way each new DataGenerator you create will have to set it and you wont forget.


About having the name as a string - I personally prefer having it "strongly typed". What I mean is that if I pass Gen . 2 instead of Gen.2 with strings I will discover this problem only in runtime. Possible other ways (if you want, because a simple string is fine too - a matter of taste):

  • Replace strings with an enum
  • Have a static class with static readonly strings for all your values - then in your code use those values. You get the benifits of the intellisense and of not getting the string wrong but better than enum - you can just still pass strings that are not in the "list" so you can add new ones as add-ons.
  • Have a RequestGenerator object, with each Generator being IDataGenerator<TGeneratorRequest>. This might be an overkill but if you have also extra information you need for the creating of a DataGenerator which differs between them then consider it .

How can I find all generators? Reflection over every implementation of the interface/every member of the namespace (unique for the generators + their interface)?

Yes, reflection can be a good way to do so. However, I would suggest to read into Dependency Injection and IoC Containers like Castle Windsor for example. There are things out there that already implement it for you, so why to re-invent the wheel :)

DI is a life changer concept in my opinion

Is it correct to call this way of working a factory, or is this some different pattern?

Yap. It is a Factory

Should the magic GetImplementations() in the factory be done via Reflection or somehow different?

See answer for question 2

like image 173
Gilad Green Avatar answered Sep 24 '22 22:09

Gilad Green


This is where constructor injection can REALLY shine. Look into dependency injection tools and employ one! It also checks your "Bonus" request.

Here's what your factory might look like with constructor injection:

public class DataFactory
{
    private Dictionary<string, IDataGenerator> generators;

    public DataFactory(IDataGenerator[] generatorReferences)
    {
        this.generators = generatorReferences
            .ToDictionary(k => k.name, v => v);
    }
    public IDataGenerator Create(string type)
    {
        IDataGenerator generator = null;
        this.generators.TryGetValue(type, out generator);
        return generator;
    }
}

Most DI software has the capability to automatically scan assemblies for implementations of a certain type (e.g. IDataGenerator) and register those with itself, when it constructs an instance of your DataFactory it'll automatically include them.

like image 22
Trevor Ash Avatar answered Sep 23 '22 22:09

Trevor Ash