2016-10-04 5 views
1

Например, у меня есть класс фабрики:C# - Как назначить enum как должно быть реализовано в абстрактном классе?

abstract class UnitFactory<T> where T:Unit 
{ 
    abstract enum UnitType; 

    public abstract T GetUnit(UnitType type); 
} 

Здесь, производный класс сусло иметь перечисление (конечно abstract enum не работает), чтобы указать, какой тип блока он может сделать, вместо того, кучу строковых констант, которые, как мне кажется, трудно обеспечить/управлять.

Так что мой вопрос в том, как я могу сделать «аннотация» enum вот так? Или, если это невозможно, какова наилучшая практика сделать что-то подобное?

Извините за мой плохой английский и мой, казалось бы, глупый вопрос.

Edit: Sample дочерний класс:

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(...); 
    } 
} 
+0

Что d oes 'abstract enum' означает? Перечисления не поддерживают наследование. Они всего лишь значения –

+0

enum - тип значения, к сожалению, вы ничего не можете извлечь из него –

+2

Это звучит как случай проблемы [XY] (http://meta.stackexchange.com/questions/66377/what- это-The-ху-проблема). Имея проблему с X, предполагая, что Y - это решение (абстрактные перечисления), и когда это не работает, запрос Y, а не фактическая проблема X. Какова фактическая проблема? –

ответ

2

Вы должны определить два универсальных типов для вашей абстрактной фабрики

public abstract class UnitFactory<TType, TUnit> where TUnit:Unit 
{ 
    public abstract TUnit GetUnit(TType type); 
} 

И тогда вам нужно выставить лучников типа за пределами вашего лучник завода, в противном случае он не будет использоваться.

public enum ArcherType { Longbowman, Crossbowman } 

И в конечном итоге создать фабрику лучников.

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:

Вместо использования статических экземпляров и клонирования можно использовать 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); 
+0

Блестящий !!!! Я действительно не думал о передаче enum в качестве параметра типа. Но могу ли я ограничить параметр первого типа перечислением? – Tr1et

+0

Хотя вы должны быть в порядке только с одним перечислением UnitType и регистрировать делегатов разных конструкторов только на один завод с разными значениями enum ... так что можно просто использовать UnitFactory.Create (UnitType.LongBowman). Также нет необходимости в клонировании и т. Д. –

+0

Добавлен пример регистрации funcs на фабрике, который сделает вашу жизнь проще, не беспокоясь о клонировании. –

0

Требуя каждый бетонный завод, чтобы выставить свой собственный список типов означает, что все конкретные реализации будут привязаны к этой фабрике в одиночку.

Другими словами, нет абсолютно никакой пользы при этом:

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> 
{ 
    ... 
}