Tr1et
Tr1et

Reputation: 895

C# - How to designate an enum as must be implemented in abstract class?

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

Answers (2)

Janne Matikainen
Janne Matikainen

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

vgru
vgru

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

Related Questions