1

I'm struggling to create a factory pattern where not the factory, but the classes determine which class needs to be used for creating the object. To make it more complicated than the normal factory pattern, the classes often need to create an instance of a subclass instead.

I would like to do something similar to this:

class Observation {
  public int NumLegs;
  public bool HasWings;
  public NumChromosomes;
  //  etc. 
}

class Organism {
  public static Organism CreateByObservation(Observation obs) {
    if (obs.numLegs == 4) return new Mammal.CreateByObservation(obs);
    else if (obs.HasWings) return new Bird.CreateByObservation(obs);
    // etc.
  }
}

class Mammal: Organism {
  public static override Mammal CreateByObservation(Observation obs) {
    if (obs.NibblesALot) return new Rodent.CreateByObservation(obs);
    else if (obs.Whinnies) return new Horse();
    // etc.
  }
}

class Rodent: Mammal {
  public static override Rodent CreateByObservation(Observation obs) {
    if (obs.Color == Colors.GRAY) return new Mouse();
    else // ... you get the idea
  }
}

// usage:
var myobservation = new Observation() { ... };
var myorganism = new Organism.CreateByObservation(myobservation);
tbName = myorganism.GetName();

For obvious reasons I'd like to encapsulate the determining code in the subclasses (Organism should know nothing about rodents), but since overriding static methods is not allowed in C# I can't use the above code.

What would be the best way to handle this?

ErikvdW
  • 113
  • 1
  • 6
  • 5
    Um, don't make it static then. – DavidG Apr 01 '18 at 22:08
  • @DavidG That's not very helpful. I'm not even sure what would be the best way to do this if I avoid making it static. You read the bit about the subclasses, right? – ErikvdW Apr 01 '18 at 22:21
  • 2
    Well first you're asking how to override a method, to do that you can't use statics. Second, why do you even want to override anyway? If it's static, you would always need to specify the full class name which makes overriding pointless. – DavidG Apr 01 '18 at 22:23
  • @DavidG, you're right, I could make it not override, but that doesn't allow me to use polymorphism on that method, nor would it allow me to force all Organism-derived classes to have this method. – ErikvdW Apr 01 '18 at 23:02
  • You need an instance to override anything. Static is exactly the opposite of polyphormism. You either want inheritance or you want static methods – Camilo Terevinto Apr 01 '18 at 23:15
  • Also, you can't override a return type, for that you would need to switch to using generics. – DavidG Apr 01 '18 at 23:42

2 Answers2

3

You might consider separating your organism factories from your organisms data classes. This means you couple your factory to your data classes, but you don't couple your data classes to each other (apart from by inheritance).

For example:

//Organism objects

class Organism { ... } 
class Mammal : Organism { ... } 
class Rodent : Mammal { ... } 

//Factory

class OrganismFactory 
{
    public static Organism CreateByObservation(Observation obs) 
    {
        if (obs.numLegs == 4) return new Mammal.CreateByObservation(obs);
        else if (obs.HasWings) return new Bird.CreateByObservation(obs);
        // etc.
    }
}
James Wood
  • 17,286
  • 4
  • 46
  • 89
3

If your application grows you can look into Assembly Scanning. Basically you could create various classes that implement this interface, one single class per possible Organism subtype

interface IOrganismSubFactory
{
    Organism Create(Observation observation);
}

For example one "sub" factory could look like this

class MammalFactory : IOrganismSubFactory
{
    public Organism Create(Observation observation)
    {
        if (observation.NumLegs != 4) return null;

        return new Mammal(obs);
    }
}

Now to tie things together, you scan the assembly and get all possible subfactories and unite them in the factory like so

class OrganismFactory
{
    IOrganismSubFactory[] _subFactories;

    public OrganismFactory()
    {
        _subFactories = Assembly.GetCallingAssembly().GetTypes()
                                .Where(t => typeof(IOrganismSubFactory).IsAssignableFrom(t))
                                .Select(t => (IOrganismSubFactory)Activator.CreateInstance(t))
                                .ToArray();
    }

    public Organism Create(Observation observation)
    {
        return _subFactories.Select(factory => factory.Create(observation)).FirstOrDefault(instance => instance != null);
    }
}

You can then use this Factory to create (well try to create) the instances

var factory = new OrganismFactory();
var organism = factory.Create(observation);
// organism is null if no factory was found

The reason why this might be a good idea is that you code is clearly separated, but you have to remember to create factories for each possible class.

One more advantage is that you could use a Dependency Injection library to wrap this logic and then enable you to use IoC. Basically this means that the subfactories could declare dependencies to other classes that get automatically injected into those subfactories.

This is probably beyond the scope of your current problem but still interesting to know

Dennis Kuypers
  • 546
  • 4
  • 16