我有一个抽象的基类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
本身是一个抽象类,它将根据Enum值返回一个具体的BloodTest
对象。为了清楚起见,我想要一个BloodTestType
和一个UrineTestType
(未显示)枚举。由于CreateTest
方法是抽象的,当我想要创建BloodTestType
和BloodTest
枚举时,如何确保我可以将UrineTestType
传递给我创建UrineTest?
答案 0 :(得分: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;
就我个人而言,我可能会将这两个工厂分解为没有基础的单独类,或者考虑使用接口。处理您希望作为“常见”方法的特定参数很困难。
答案 1 :(得分: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.
}
}
答案 2 :(得分:0)
只需在重写方法中测试类型安全性:
public override LabTest CreateTest(Enum e)
{
BloodTest bt = null;
if(!e.GetType.Equals(typeof(BloodTestType)))
{
// throw type exception of your choice
}
}
答案 3 :(得分: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;
}
//...
}