C# 过于复杂的工厂方法-有解决方案吗?
在创建某些对象族时,工厂方法是隐藏复杂性的好方法。好的但当工厂方法本身开始变得复杂时会发生什么呢 例如,我需要基于两个或多个标志/属性/值创建一个对象,如下所示:C# 过于复杂的工厂方法-有解决方案吗?,c#,java,design-patterns,C#,Java,Design Patterns,在创建某些对象族时,工厂方法是隐藏复杂性的好方法。好的但当工厂方法本身开始变得复杂时会发生什么呢 例如,我需要基于两个或多个标志/属性/值创建一个对象,如下所示: public class MyClassFactory { public static IMyClass GetClass(int prop1, int prop2, int prop3) { switch(prop1) { case (1):
public class MyClassFactory
{
public static IMyClass GetClass(int prop1, int prop2, int prop3)
{
switch(prop1)
{
case (1):
switch(prop2)
{
case(1):
if(prop3 == x)
return new ImplMyClassA();
else
return new ImplMyClassB();
...
//etc ad infinitum
}
}
}
}
这很快就会变得难看。好的,所以你对客户隐藏了复杂性,但是你的工厂代码正在成为维护的头痛问题
对于这个问题还有什么其他解决方案?是否存在某种多值查找模式,使其更易于阅读和维护 您可以将其转换为从属性元组(封装在一个类中)到类名的映射,然后使用反射进行实例化。或者,地图的层次结构。在Java中,我将使用Spring在XML配置文件中定义这样的映射;也许在C语言中也有类似的方法来实现这一点。把这几部分放在一起。:) 将参数映射到匿名创建者
Class propertySet{
int prop1
int prop2
int prop3
public equals(..){... }
}
interface creator {
IMyClass create()
}
Map<propertySet, classCreator> creatorsMap = new HashMap();
static {
creatorsMap.add(new propertySet(1,2,3), new creator() { ... create() {return ImplMyClassA(); } });
......
creatorsMap.add(new propertySet(7,8,9), new creator() { ... create() {return ImplMyClassB();} });
}
public IMyClass create(x,y,z) {
return creatorsMap.get(new propertySet(x,y,z)).create()
}
类属性集{
int prop1
int prop2
int prop3
公共等于(..){…}
}
接口创建者{
IMyClass create()
}
Map creatorsMap=新建HashMap();
静止的{
add(newpropertyset(1,2,3),newcreator(){…create(){return ImplMyClassA();}}});
......
add(newpropertySet(7,8,9),new creator(){…create(){return ImplMyClassB();}});
}
公共IMyClass创建(x,y,z){
返回creatorsMap.get(新属性集(x,y,z)).create()
}
有很多选择,但这取决于它的扩展方式。这些条件还有哪些其他值/逻辑
一种选择是使用组合开关
public static IMyClass getClass(int prop1, int prop2, int prop3) {
switch(prop1*1000000+prop2*1000+prop3) {
case 1001001:
}
}
一种选择是使用反射
public static IMyClass getClass(int prop1, int prop2, int prop3) {
Method m = MyClassFactory.class.getMethod("create"+prop1+"_"+prop2+"_"+prop3);
return (IMyClass) m.invoke(this);
}
public static IMyClass create1_1_1() {
return new ...;
}
根据3个属性的性质,可以创建定义每个类的自定义属性。所以你会有这样的课程:
[IMyClass(Prop1 = 1, Prop2 = 3, Prop3 = 4)]
public class Blah : IMyClass
[IMyClass(Prop1 = 4, Prop2 = 5, Prop3 = 6)]
public class Blah2 : IMyClass
然后,在factory方法中,您可以使用反射在IMyClass
的所有实现中循环,检索它们的IMyClassAttribute
实例,并检查属性是否匹配。这使您不必在两个位置保留映射,也不必在类定义本身中保留类的映射
编辑仅供参考,这是基于C#的,我不知道Java是否有类似的功能(尽管我确信它有)如果您的规则可能变得比简单的属性比较更复杂,您可能希望留下一个选项来做更多的事情,例如:
interface IFactoryRule
{
bool CanInstantiate(PropertySet propSet);
}
简单的实现将如下所示:
// compares property set to given parameters
public SimpleRule : IFactoryRule
{
private readonly int a,b,c;
public SimpleRule(a,b,c) { ... }
public bool CanInstantiate(PropertySet propSet)
{
return
propSet.a == a &&
propSet.b == b &&
propSet.c == c;
}
}
但您也可以创建任何类型的复杂自定义规则:
// compares property set using a delegate
public ComplexRule : IFactoryRule
{
private readonly Func<PropertySet, bool> _func;
public ComplexRule(func) { ... }
public bool CanInstantiate(PropertySet propSet)
{
return _func(propSet);
}
}
如果这没有用,我很抱歉,但我忍不住要指出,这种东西很容易用F#写,而且可以很容易地从C#调用。事实上,这个F#代码的签名与问题中的示例方法的签名相同——它看起来与调用它的C#代码相同
module MyClassFactory =
let GetClass = function
| 1, 1, 1 -> ImplMyClassA() :> IMyClass
| 1, 1, 2 -> ImplMyClassB() :> IMyClass
| 1, 1, _ -> ImplMyClassC() :> IMyClass
| 2, 1, 1 -> ImplMyClassD() :> IMyClass
| 2, 1, _ -> ImplMyClassE() :> IMyClass
| 2, 2, _ -> ImplMyClassF() :> IMyClass
| _ -> ImplMyClassDefault() :> IMyClass
如果你不能用MEF,我猜你也不能用F。然而,事实上,“某种多值查找模式可能会使它更易于阅读和维护”-它在C#中并不存在。你能使用C#吗?如果是这样的话,你可以做一些特定于语言的事情来简化你的生活,即MEF和泛型…我在MEF的使用方面受到限制,但不是泛型-我想看看一个例子?看起来你得到了很多建议。MEF是我最喜欢的解决这个问题的方法,对你的限制太糟糕了。我的博客()上有几个简单的工厂示例,包括使用MEF和泛型。如果我是你的话,我会使用一个映射和一个抽象工厂实现。我曾经考虑过映射的层次结构,如果这是我能做的最好的,那就这样吧。这仍然有点笨重,尽管在嵌套开关上有了一定的改进。反射解决方案看起来有点过分:)哇,这太糟糕了。:)我更喜欢映射(prop.tuples to factory方法)而不是这个解决方案。@Andrew Frolov,它需要添加两行特殊处理代码。创建数据结构以反映Java为您所做的事情是过分的@格罗,我猜你喜欢编写和维护大量代码@彼得:使用反射和弱类型来调用方法并不是解决任何问题的最愉快的方法。是的,
int
是非常容易序列化为字符串的类型。维护大量代码?不,我讨厌它。特别是当我需要重构一堆代码时,因为愚蠢的C不允许我命名方法create5.34-1.11-####()
。我喜欢这样。。你能举例说明一下你是怎么称呼它的吗?
IMyClass instance = MyClassFactory.Create(propSet);
module MyClassFactory =
let GetClass = function
| 1, 1, 1 -> ImplMyClassA() :> IMyClass
| 1, 1, 2 -> ImplMyClassB() :> IMyClass
| 1, 1, _ -> ImplMyClassC() :> IMyClass
| 2, 1, 1 -> ImplMyClassD() :> IMyClass
| 2, 1, _ -> ImplMyClassE() :> IMyClass
| 2, 2, _ -> ImplMyClassF() :> IMyClass
| _ -> ImplMyClassDefault() :> IMyClass