Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/384.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 过于复杂的工厂方法-有解决方案吗?_C#_Java_Design Patterns - Fatal编程技术网

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