#c# #enums #factory
#c# #перечисления #фабрика
Вопрос:
Например, у меня есть фабричный класс:
abstract class UnitFactory<T> where T:Unit
{
abstract enum UnitType;
public abstract T GetUnit(UnitType type);
}
Здесь производный класс должен иметь перечисление (конечно abstract enum
, не работает), чтобы указать, какую единицу он может создать, вместо набора строк, которые, я думаю, трудно применять / управлять.
Итак, мой вопрос в том, как мне создать «абстрактное» перечисление, подобное этому? Или, если это невозможно, как лучше всего сделать что-то подобное?
Извините за мой плохой английский и мой, казалось бы, глупый вопрос.
Редактировать: пример дочернего класса:
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(...);
}
}
Комментарии:
1. Что это
abstract enum
значит? Перечисления не поддерживают наследование. Это просто значения2. перечисление — это тип значения, к сожалению, поэтому вы ничего не можете извлечь из него
3. Это похоже на случай проблемы XY . Возникает проблема с X, предполагая, что Y является решением (абстрактные перечисления), и когда это не работает, запрашивается Y, а не фактическая проблема X. В чем фактическая проблема?
4. Я понимаю, я просто хочу иметь что-то вроде «абстрактного перечисления», должно быть реализовано перечисление.
5. @Tr1et: что такое «реализованное перечисление»? Приведите пример того, как вы могли бы 1) создать несколько конкретных фабрик и 2) вызвать
GetUnit
несколько комбинаций фабрик / типов единиц.
Ответ №1:
Вы должны определить два общих типа для вашей абстрактной фабрики
public abstract class UnitFactory<TType, TUnit> where TUnit:Unit
{
public abstract TUnit GetUnit(TType type);
}
И затем вам нужно предоставить тип archer за пределами вашей фабрики archer, иначе его нельзя было бы использовать.
public enum ArcherType { Longbowman, Crossbowman }
И в конечном итоге создайте фабрику archer.
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(...);
}
}
}
Редактировать:
Вместо использования ваших статических экземпляров и клонирования вы можете использовать Func для создания отдельных экземпляров каждого модуля.
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]();
}
}
И используйте это
var archerFactory = new ArcherFactory();
var crossbowMan = archerFactory.GetUnit(ArcherType.Crossbowman);
Комментарии:
1. Блестяще!!!! Я действительно не думал о передаче перечисления в качестве параметра типа. Но могу ли я ограничить параметр первого типа перечислением?
2. Хотя вы должны быть в порядке с одним перечислением UnitType и регистрацией разных делегатов конструктора только для 1 фабрики с разными значениями enum… так что можно просто использовать UnitFactory. Создать(UnitType. LongBowman). Также нет необходимости в клонировании и т. Д.
3. Добавлен пример регистрации функций на фабрике, что облегчит вашу жизнь, не беспокоясь о клонировании.
Ответ №2:
Требование, чтобы каждая конкретная фабрика предоставляла свой собственный список типов, означает, что все конкретные реализации будут привязаны только к этой фабрике.
Другими словами, нет абсолютно никакой пользы в выполнении:
var longbowman = archerFactory.GetUnit(ArcherFactory.UnitType.Longbowman);
var crossbowman = archerFactory.GetUnit(ArcherFactory.UnitType.Crossbowman);
по сравнению с просто:
// this is better than a huge switch/case method
var longbowman = archerFactory.GetLongbowman()
var crossbowman = archerFactory.GetCrossbowman();
Чтобы шаблон абстрактной фабрики имел смысл, вам необходимо определить тип отдельно от фабрики, т.е.
enum ArcherType { Longbowman, Crossbowman }
interface IFactory<Ttype, Tvalue>
{
Tvalue GetUnit(Ttype);
}
interface IArcherFactory : IFactory<ArcherType, Archer>
{
...
}