Reputation: 895
For example, I have a factory class:
abstract class UnitFactory<T> where T:Unit
{
abstract enum UnitType;
public abstract T GetUnit(UnitType type);
}
Here, the derived class must have a enum (of course abstract enum
doesn't work) to indicate which kind of unit it can make, instead of a bunch of string consts which I think is hard to enforce/manage.
So my question is how do I make an "abstract" enum like this? Or if it's not possible, what is the best practice to do something similar?
Sorry for my bad English and my seemingly stupid question.
Edit: Sample child class:
class ArcherFactory : UnitFactory<Archer>
{
private static Archer _baseLongbowman = ....;
private static Archer _baseCrossbowman = ....;
// Child class must have a implementation of UnitType enum to
// tell the user that it can only make those kind of units.
public enum UnitType{ Longbowman, Crossbowman }
public override Archer getUnit(UnitType type)
{
if (type == UnitType.Longbowman) return _baseLongbowman.Clone(...);
return _baseCrossbowman.Clone(...);
}
}
Upvotes: 1
Views: 541
Reputation: 5121
You should define two generic types for your abstract factory
public abstract class UnitFactory<TType, TUnit> where TUnit:Unit
{
public abstract TUnit GetUnit(TType type);
}
And then you need to expose the archer type outside your archer factory, otherwise it would not be usable.
public enum ArcherType { Longbowman, Crossbowman }
And eventually create the archer factory.
public class ArcherFactory : UnitFactory<ArcherType, Archer>
{
private static Archer _baseLongbowman = ....;
private static Archer _baseCrossbowman = ....;
public override Archer GetUnit(ArcherType type)
{
switch (type)
{
case ArcherType.Crossbowman:
return _baseCrossbowman.Clone(...);
default:
return _baseLongbowman.Clone(...);
}
}
}
Edit:
Instead using your static instances and cloning you could use Func to create your separate instances of each unit.
public class ArcherFactory : UnitFactory<ArcherType, Archer>
{
public ArcherFactory()
{
this.Register(ArcherType.Longbowman, () => new Archer(...));
this.Register(ArcherType.Crossbowman, () => new Archer(...));
}
}
public abstract class UnitFactory<TType, TUnit>
{
private readonly Dictionary<TType, Func<TUnit>> factoryMethods = new Dictionary<TType, Func<TUnit>>();
protected void Register(TType type, Func<TUnit> constructorFuc)
{
// perform some sanity checks
this.factoryMethods.Add(type, constructorFuc);
}
public TUnit GetUnit(TType type)
{
// perform some sanity checks
return this.factoryMethods[type]();
}
}
And use that
var archerFactory = new ArcherFactory();
var crossbowMan = archerFactory.GetUnit(ArcherType.Crossbowman);
Upvotes: 2
Reputation: 51244
Requiring each concrete factory to expose its own list of types means all concrete implementations will be tied to that factory alone.
In other words, there is absolutely no benefit in doing:
var longbowman = archerFactory.GetUnit(ArcherFactory.UnitType.Longbowman);
var crossbowman = archerFactory.GetUnit(ArcherFactory.UnitType.Crossbowman);
compared to simply:
// this is better than a huge switch/case method
var longbowman = archerFactory.GetLongbowman()
var crossbowman = archerFactory.GetCrossbowman();
For the abstract factory pattern to make sense, you need to have the type defined separately from the factory, i.e.
enum ArcherType { Longbowman, Crossbowman }
interface IFactory<Ttype, Tvalue>
{
Tvalue GetUnit(Ttype);
}
interface IArcherFactory : IFactory<ArcherType, Archer>
{
...
}
Upvotes: 0