Firstly, I struggled with what to title this question, so please edit it if you think you've got a better idea.
This seems to be a problem I run into now and again and end up doing something smelly to circumvent. The general form seems to be that I have a non-generic class meant for getting an instance of something (i.e. a Factory) but there needs to be decisions made about what type the instance should have. I think the example below illustrates what I'm going for, but you'll notice it doesn't compile because in the FooBarFactory
I can't use AbstractThing
for T
as abstracts aren't allowed in that context. So how can I let the FooBarFactory
determine, at runtime, which type the Generator<T>
should use for T?
public class FooBarFactory
{
private Generator<AbstractThing> _generator;
public AbstractThing GetThing(string data, ThingType type)
{
switch (type)
{
case ThingType.Foo:
_generator = new FooGenerator();
break;
case ThingType.Bar:
_generator = new BarGenerator();
break;
case ThingType.None:
return null;
default:
throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
return _generator.Generate(data);
}
}
public abstract class Generator<T> where T : AbstractThing, new()
{
protected T GeneratedThing = new T();
protected abstract void MapDataToThing(string data);
public T Generate(string data)
{
MapDataToThing(data);
return GeneratedThing;
}
}
public class FooGenerator : Generator<Foo>
{
protected override void MapDataToThing(string data)
{
GeneratedThing.AbstractProp = ParseAbstract(data);
GeneratedThing.FooProp = ParseFoo(data);
}
private static string ParseAbstract(string data)
{
throw new NotImplementedException();
}
private static string ParseFoo(string data)
{
throw new NotImplementedException();
}
}
public class BarGenerator : Generator<Bar>
{
protected override void MapDataToThing(string data)
{
GeneratedThing.AbstractProp = "Base property in Bar";
GeneratedThing.BarProp = data.Substring(4, 3);
}
}
public abstract class AbstractThing
{
public string AbstractProp { get; set; }
}
public class Foo : AbstractThing
{
public string FooProp { get; set; }
}
public class Bar : AbstractThing
{
public string BarProp { get; set; }
}
public enum ThingType
{
None = 0,
Foo = 1,
Bar = 2
}
The main purpose of generics is to provide compile-time type safety. It makes no sense to "let the FooBarFactory determine, at runtime, which type the Generator<T>
should use for T
".
My intention was to enforce that the factory returns an AbstractThing.
This is enforced by the factory method having a return type of AbstractThing
.
What would you recommend here? Should the factory simply have a method for each subtype of AbstractThing?
The GetThing method, i.e. the factory method, should simply return an AbstractThing
based on some logic of yours, i.e. it is the non-generic factory itself that decides whether to return a Foo or a Bar object. Generics doesn't really apply here.
Or maybe should there be no instance of Generator in the factory, instead newing up the appropriate Generator within each case block?
You don't necessarily need any generator class. You could implement the logic that decides whether a Foo
or a Bar
should be created directly in the factory method itself if you want to. The factory takes N number of parameters as input and return a concrete implementation of the AbstractThing
type based on some logic that is abstracted away in the factory.
Hope that makes sense.