带类型参数的通用工厂
本文关键字:工厂 类型参数 | 更新日期: 2023-09-27 17:51:22
我有以下情况
我的Factory类需要根据CreateStrategy函数的输入字符串参数创建适当的Strategy对象。
Strategy1、Strategy2等都是从一个通用的StrategyBase类派生出来的。然而,每种策略都有不同的验证机制,即Factory类的类型参数。然而,strategyvalidator不是任何通用类型,并且具有不同的接口。
因此,在下面的代码中,我无法在StrategyValidator类型上指定任何公共约束。
我是c#新手,因此不确定是否存在任何机制来克服这个设计问题。请建议
public class Factory
{
//Create the appropriate Concrete Implementation class based on the type
public static StrategyBase CreateStrategy<StrategyValidator>(String Type)
{
StrategyBase EnumImp = null;
// WMI based implementation
if (Type == "Type1")
{
s = Strategy1<StrategyValidator>.Instance;
}
else if (Type = "Type2")
{
s = Strategy2<StrategyValidator>.Instance;
}
return s;
}
private StrategyBase s;
}
这是预期的用法
Factory f = new Factory();
f.CreateStrategy<WMIValidator>("WMI");
f.CreateStrategy<ABCDValidator>("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<T> : ProductBase where T : S1
{
};
class Product2<T> : ProductBase where T : S2
{
};
public class Factory
{
public ProductBase Create<T>(String Type)
{
if (Type == "P1")
return new Product1<T>();
else if (Type == "P2")
return new Product2<T>();
}
};
class Program
{
static void Main(string[] args)
{
Factory f = new Factory();
ProductBase s = f.Create<S1Concrete>("Type1");
}
}
}
我得到的错误是
类型'T'不能用作泛型类型中的类型参数'T'或方法'TestCSharp.Product1'。没有拳击转换或从'T'到'TestCSharp.S1'的类型参数转换。
我真的不完全理解你的场景,但据我所知,你正在使用的工厂模式必须使用反射来实例化产品。这有点难看,因为它没有给消费者任何提示,告诉他们给定的产品名称可以使用哪些策略类型。
public class Factory
{
public ProductBase Create<T>(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
做什么。你似乎想把你的逻辑分成两部分。然后,您想通过使用泛型来再次对其进行耦合,但正如您所注意到的,它不起作用。
考虑一个类似于上面的场景——但是每个实现IStrategy
的类都有一个而不是两个有副作用的方法(即打印字符串)。当允许的类型范围有一些共同点时,使用泛型。在我刚才提到的例子中,两者都有一个方法返回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
具有不同的验证机制。然而,在我看来,你在相同的方法和上下文中使用它们(因此是相同的参数和变量),所以一定有一些东西使它们相似。尽管如此,已经以我们需要的方式定义了IStrategy
,我们可以将其用作Create<T>
的约束。因此,Factory
变成:
public class Factory
{
public ProductBase Create<T>(String Type) where T : IStrategy
{
if (Type == "P1")
return new Product1<T>();
else if (Type == "P2")
return new Product2<T>();
return null;
}
};
但仍有一种情况。如果您不希望Product1
使用S2
作为泛型类型来调用,或者Product2
使用S1
作为泛型,那么为什么首先使用泛型呢?您可以轻松地将产品与其相关策略结合起来,并且还可以显着简化代码。
如果我遗漏了什么(或整个问题),请留下评论,我会尽量调整我的答案。
EDIT:既然现在您已经重新定义了示例并使用S1
和S2
作为接口,我可以理解您的意思。一种方法是为Factory.Create
定义多个泛型类型和约束。例子:
public ProductBase Create<T1, T2>(String Type) where T1 : S1 where T2 : S2
这是不可能的,否则,正如你正确地说,因为没有S1
和S2
的共同祖先,可以被你的Product
类接受。
您可以将函数更改为将StrategyValidator作为类型。
从public static StrategyBase CreateStrategy<StrategyValidator>(String Type)
public static StrategyBase CreateStrategy<T>(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<string, Type>
您考虑过重载Create<>函数吗?我现在没有VisualStudio,但是下面的代码会为您的情况工作吗?
namespace ... {
// ... other code here...
public class Factory {
public Product1<T> Create<T>() where T : S1 {
return new Product1<T>();
}
public Product2<T> Create<T>() where T : S2 {
return new Product2<T>();
}
}
class Program {
static void Main(string[] args) {
Factory f = new Factory();
ProductBase s = f.Create<S1Concrete>();
}
}
}
另外,您可能希望将类型约束移到较低的级别。考虑编写一个抽象基类ProductBase(继承自IProductBase
接口?),如下所示:
class ProductBase<T> : IProductBase where T : IStrategy { }
这可能有助于缓解你的一些头痛