2013-12-16 2 views
2

У меня есть абстрактный базовый класс, TestFactory который выглядит как:Передача перечислений в качестве параметра из абстрактного класса

public abstract class TestFactory 
    { 
     //static method that can create concrete factories 
     public static TestFactory CreateTestFactory(FactoryType factoryType) 
     { 
      TestFactory factory = null; 
      switch (factoryType) 
      { 
       case FactoryType.Blood: 
        factory = new BloodTestFactory(); 
        break; 
       case FactoryType.Urine: 
        factory = new UrineTestFactory(); 
        break; 
       default: 
        break; 
      } 
      return factory; 
     } 
     //BloodTestFactory and UrineTestFactory are concrete types 
     //that will need to create their own tests 
     //this enum parameter needs to 'switch' between 
     //BloodTestType and UrineTestType 
     public abstract LabTest CreateTest(Enum e); 
    } 
    public enum FactoryType 
    { 
     Blood,Urine 
    } 

Так что этот класс создает бетонный завод, как:

public class BloodTestFactory :TestFactory 
    { 

     //both BloodTestFactory and UrineTestFactory will create a LabTest object 
     //I would like to have a BloodTestType and UrineTestType enum, what do I need 
     //to do to pass a generic Enum as a parameter and then switch on BloodTestType 

     public override LabTest CreateTest(Enum e) 
     { 
      BloodTest bt = null; 
      //switch (e) 
      //{ 
      // default: 
      //  break; 
      //} 
      //creation logic here 
     } 
    } 
    public enum BloodTestType 
    { 
     H1AC,Glucose 
    } 

BloodTest сам по себе является абстрактный класс, который вернет конкретный объект BloodTest на основе значения Enum. Для ясности я хотел бы иметь список BloodTestType и UrineTestType (не показан). Поскольку метод CreateTest является абстрактным, как я могу удостовериться, что могу передать ему BloodTestType, когда хочу создать BloodTest s и перечислить UrineTestType, когда я хочу создать UrineTest?

+0

Разве вы не можете просто конвертировать родовое '' Enum e'' в бетон '' BloodTestType'' внутри '' метода CreateTest''? – Kippie

+0

@ Сильверминд, что было бы проще всего сделать. Мне было просто любопытно, есть ли более «элегантный» способ сделать это. Я знаю, что перечисления не наследуют, но я думал, что может быть что-то в этом роде. Не то, чтобы вы могли знать, но на самом деле последние из перечислений, которые могут потребоваться, могут быть довольно большими. – wootscootinboogie

+1

Чем больше я смотрю на этот вопрос, тем больше я думаю, что ваша архитектура просто ошибается. Вы пытаетесь сделать переключатель на перечислении, чтобы определить, какой тест выполнить, но вся точка шаблонов, например Factory factory/abstract factory, заключается в том, чтобы удалить такую ​​логику ветвления из вашего кода. Таким образом, вы получите классы «GlucoseBloodLabTest», «H1ACBloodLabTest», «SomeUrineLabTest» и т. Д., Все наследуемые от базового класса «LabTest». Если вы хотите узнать, как изменить свой код, чтобы сделать это другим способом, отправьте сюда, и я буду работать над отдельным ответом для вас. – Jamiec

ответ

4

Я не уверен, если это лучший подход принять это требование, я оставлю шаблон обсуждение дизайна для комментариев/другие ответы.

Я бы не стал делать это таким образом, однако, чтобы сделать код, который у вас есть, я бы представил общий параметр уровня класса на TestFactory для enum.

public abstract class TestFactory<TTestType> 
{ 
    public abstract LabTest CreateTest(TTestType testType); 
} 

Производные классы затем просто указать общий аргумент:

public class BloodTestFactory : TestFactory<BloodTestType> 
{ 
    public override LabTest CreateTest(BloodTestType e) 
    { 
    } 
} 

Обратите внимание, что если вы используете что-то вроде Unconstrained Melody вы не получите много поддержки ограничений универсального типа на enum.

отметить также, что это сейчас делает его трудно ссылаться на общий базовый класс, потому что нужно закрыть общий аргумент:

TestFactory baseReference = myBloodTestFactory; // Not possible. 

TestFactory<BloodTestType> baseReference = myBloodTestFactory; 

Лично я бы, вероятно, разлагать эти два завода в отдельные классы без основания, или изучите использование интерфейса. Трудно справиться с конкретными параметрами в том, что вы хотели бы использовать как «обычные» методы.

+0

Этот * вид * работает, за исключением того, что метод CreateTestFactory теперь также должен принимать общий тип, отрицая использование 'FactoryType'. – Jamiec

+0

@Jamiec Да, этот метод должен возвращать 'TestFactory ', как вы говорите, это затем начинает делать подход спорным. –

+0

@wootscootinboogie Не уверен. В настоящий момент я бы рассмотрел необходимость базового класса. Метод, который вы хотите выявить базовым классом, должен принимать специфические аргументы производного класса, которые мне поражают необходимость в нем. –

0

Просто тест на безопасность типа в вашем переопределен метод:

 public override LabTest CreateTest(Enum e) 
     { 
      BloodTest bt = null; 
      if(!e.GetType.Equals(typeof(BloodTestType))) 
      { 
       // throw type exception of your choice 
      } 
     } 
0

Просто добавьте проверку аргумента в свой базовый класс:

public abstract class TestFactory 
{ 
    //static method that can create concrete factories 
    public static TestFactory CreateTestFactory(FactoryType factoryType) 
    { 
     if (!Enum.IsDefined(typeof(FactoryType), factoryType) 
     { 
      throw InvalidEnumArgumentException(...); 
     } 

     TestFactory factory = null; 
     switch (factoryType) 
     { 
      case FactoryType.Blood: 
       factory = new BloodTestFactory(); 
       break; 
      case FactoryType.Urine: 
       factory = new UrineTestFactory(); 
       break; 
      default: 
       break; 
     } 
     return factory; 
    }  
    //... 
} 
1

Существует хороший способ сделать это, и это приводит к коду, такие как:

var client = new LabTestClient(); 
client.Run<BloodTestFactory,BloodTestType>(BloodTestType.H1AC); 
client.Run<BloodTestFactory,BloodTestType>(BloodTestType.Glucose); 
// outputs 
// BloodTest: H1AC 
// BloodTest: Glucose 

Вот остальные код, мы надеемся, его вполне объяснительно, как его использовать/расширить его до ваших потребностей - есть конкретные классы для каждого типа лабораторных тестов.

Сначала клиент и абстрактные классы

public class LabTestClient 
{   
    public void Run<TFactory, TInput>(TInput input) where TFactory : LabTestFactory, new() 
    { 
     LabTestFactory factory = new TFactory();; 
     LabTest labTest = factory.CreateLabTest(); 
     labTest.Run(input); 
    } 

} 

public abstract class LabTest 
{   
    public abstract void Run<TInput>(TInput input); 
} 

public abstract class LabTestFactory 
{ 
    public abstract LabTest CreateLabTest(); 
} 

Тогда конкретная реализация:

public enum BloodTestType{ H1AC,Glucose } 

public class BloodTest: LabTest 
{   
    public override void Run<TInput>(TInput input) 
    { 
     Console.WriteLine("BloodTest: {0}",input);    
    } 
} 

public class BloodTestFactory : LabTestFactory 
{ 
    public override LabTest CreateLabTest() 
    { 
     return new BloodTest(); 
    } 
} 

public enum UrineTestType{ A,B } 

public class UrineTest: LabTest 
{   
    public override void Run<TInput>(TInput input) 
    { 
     Console.WriteLine("UrineTest: {0}",input);    
    } 
} 

public class UrineTestFactory : LabTestFactory 
{ 
    public override LabTest CreateLabTest() 
    { 
     return new UrineTest(); 
    } 
} 

Живой пример: http://rextester.com/MUV89315

Однако, это еще не идеал, как личности тесты по-прежнему не обеспечивают их ввода - и на самом деле, когда вы приходите делать что-либо сложное в рамках теста, вы обнаружите, что не знаете, какой фактический тип из TInput есть, если вы не отбрасывают его правой перечислимого:

public class UrineTest: LabTest 
{   
    public override void Run<TInput>(TInput input) 
    { 
     var urineTestType = (UrineTestType)input; 
     // do something useful. 
    } 
} 
+0

Буду честным, это немного за пределами моей оценки! :) Очень благодарен за возможность учиться. Не могли бы вы объяснить 'где TFactory: LabTestFactory, new()' part? – wootscootinboogie

+1

'где TFactory: LabTestFactory, new()' - [общее ограничение] (http://msdn.microsoft.com/en-us/library/d5x73970.aspx), указывающее, что общий тип должен наследовать 'LabTestFactory' и должен иметь безпараметрический конструктор (позволяет сам тип вести себя как фабрика - см. в методе 'new TFactory()') – Jamiec

Смежные вопросы