具有类型参数的通用工厂
我有以下情况。
我的Factory类需要根据CreateStrategy函数的输入字符串参数创建适当的Strategy对象。
Strategy1,Strategy2等都来自一个共同的StrategyBase类。 但是,每个策略都有一个不同的validation机制,它是Factory类的类型参数。 但是,StrategyValidators不是任何常见类型,并且具有不同的接口。
因此,在下面的代码中,我无法在StrategyValidator类型上指定任何常见约束。
我是C#的新手,因此不确定是否存在任何机制来克服这个设计问题。 请建议
public class Factory { //Create the appropriate Concrete Implementation class based on the type public static StrategyBase CreateStrategy(String Type) { StrategyBase EnumImp = null; // WMI based implementation if (Type == "Type1") { s = Strategy1.Instance; } else if (Type = "Type2") { s = Strategy2.Instance; } return s; } private StrategyBase s; }
这是预期的用法
Factory f = new Factory(); f.CreateStrategy("WMI"); f.CreateStrategy("ABCD");
其中WMIValidator
和ABCDValidator
是不相关的类型,但CreateStrategy
函数创建的实际类在层次结构中相关,例如具有公共基础StrategyBase
这是一个示例代码来说明问题
namespace TestCSharp { public interface IStrategy { }; public interface S1 : IStrategy { void f1(); void f2(); }; public class S1Concrete : S1 { public void f1() { } public void f2() { } } public interface S2 : IStrategy { void f3(); void f4(); }; public class S2Concrete : S2 { public void f3() { } public void f4() { } }; public interface ProductBase { }; class Product1 : ProductBase where T : S1 { }; class Product2 : ProductBase where T : S2 { }; public class Factory { public ProductBase Create(String Type) { if (Type == "P1") return new Product1(); else if (Type == "P2") return new Product2(); } }; class Program { static void Main(string[] args) { Factory f = new Factory(); ProductBase s = f.Create("Type1"); } } }
我得到的错误是
类型’T’不能用作generics类型或方法’TestCSharp.Product1’中的类型参数’T’。 从’T’到’TestCSharp.S1’没有装箱转换或类型参数转换。
我并不完全了解你的场景,但据我所知,你使用的工厂模式必须使用reflection来实例化产品。 这有点难看,因为它没有给消费者任何关于可以使用给定产品名称的策略类型的提示。
public class Factory { public ProductBase Create(string name) { Type type; switch (name) { case "P1": type = typeof (Product1<>); break; case "P2": type = typeof (Product2<>); break; case "P3": type = typeof (Product3<>); break; default: return null; } type = type.MakeGenericType(typeof (T)); return (ProductBase) Activator.CreateInstance(type); } }
我认为在这种情况下的答案是,这取决于你想要的Product
和Strategy
。 你似乎要做的是将你的逻辑分成两个分支。 然后你想通过使用generics再次将它结合起来,但正如你所注意到的,它将无法工作。
考虑一个场景,类似于你的场景 – 但是每个实现IStrategy
类都有一个而不是两个方法,它们会产生副作用(即打印一个字符串)。 当允许的类型范围有共同点时,您使用generics。 在我刚刚提到的情况下,两者都有一个返回void并且不接受任何参数的方法; 所以我们可以为IStrategy
添加一个方法,例如:
public interface IStrategy { void ExecuteLogic(); }; public class S1 : IStrategy { public void ExecuteLogic() { OneMethod(); } void OneMethod() { Console.WriteLine("Hello"); } }; public class S2 : IStrategy { public void ExecuteLogic() { TotallyDifferentMethod(); } void TotallyDifferentMethod() { Console.WriteLine("World"); } };
现在,您还说Strategy1
和Strategy2
具有不同的validation机制。 但是,在我看来,您在相同的方法和上下文中使用它们(因此使用相同的参数和变量),因此必须有一些使它们相似的东西。 尽管如此,以我们需要的方式定义了IStrategy
,我们可以将其用作Create
的约束。 因此, Factory
成为:
public class Factory { public ProductBase Create(String Type) where T : IStrategy { if (Type == "P1") return new Product1 (); else if (Type == "P2") return new Product2 (); return null; } };
但还是有一个案例。 如果您不希望使用S2
作为generics类型调用Product1
,或者将Product2
作为通用类型调用,那么为什么首先使用generics? 您可以轻松地将产品与其相关策略相结合,并显着简化代码。
如果我错过了某些内容(或整个问题),请发表评论,我会尝试调整我的答案。
编辑 :既然你已经重新定义了你的例子,并使用S1
和S2
作为接口,我可以看到你的意思。 一种方法是为Factory.Create
定义多个generics类型和约束。 例:
public ProductBase Create(String Type) where T1 : S1 where T2 : S2
否则,正如您所说,因为没有S1
和S2
共同祖先,您的Product
类可以接受它。
您可以更改函数以将StrategyValidator作为类型。
从
public static StrategyBase CreateStrategy(String Type)
至
public static StrategyBase CreateStrategy(String Type) where T:StrategyValidator
要回答你的问题,你无法避免有条件的检查。
要简化代码,可以将不同的组合(“Type1”,“Type2”等)移动到dictionary
或配置(如果使用Dependency Injection
,然后可以反映。
例。
if (!dict.ContainsKey(key)) throw New InvalidArgumentException(); StrategyBase EnumImp = null; var instance = dict[key].MakeGenericType(typeOf(type)).GetProperty("Instance", BindingFlags.Static | BindingFlags.Public )); //dict is Dictionary
您是否考虑过重载Create <>函数? 我现在没有VisualStudio方便,但以下代码是否适用于您的情况?
namespace ... { // ... other code here... public class Factory { public Product1 Create () where T : S1 { return new Product1 (); } public Product2 Create () where T : S2 { return new Product2 (); } } class Program { static void Main(string[] args) { Factory f = new Factory(); ProductBase s = f.Create(); } } }
此外,您可能希望将类型约束移动到较低级别。 考虑编写一个抽象的基础ProductBase类(inheritance自IProductBase
接口?),如下所示:
class ProductBase : IProductBase where T : IStrategy { }
这可能有助于减轻您的一些头痛。