2011-04-28 73 views
2

工厂方法是创建特定对象系列时隐藏复杂性的好方法。精细。但是当工厂方法开始变得复杂时会发生什么?过于复杂的工厂方法 - 任何解决方案?

例如,我需要建立基于两个或多个标志/属性/值的对象,像这样:

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 
      } 
     } 
    } 
} 

这很快变得难看。好的,所以你的客户隐藏起来很复杂,但你的工厂代码正在成为维护头痛。

此问题还有其他哪些解决方案?是否有某种多值查找模式,可能会使这一点更易于阅读和维护?

+0

你可以使用C#吗?如果是这样,你可以做一些语言特定的事情,让你的生活更轻松,即MEF和泛型... – BrandonZeider 2011-04-28 13:02:56

+0

我受MEF使用限制,但不是泛型 - 我很想看一个例子吗? – MalcomTucker 2011-04-28 13:07:21

+0

看起来你有很多建议。 MEF是我最喜欢的方式来解决这个问题,对你的限制太糟糕了。我的博客上有几个简单的工厂示例(http://BrandonZeider.me),包括使用MEF和泛型。如果我是你,我会使用地图和抽象工厂实现。 – BrandonZeider 2011-04-28 13:15:09

回答

2

如果你的规则都不能变得比简单更复杂比较属性,你可能想留下一个选项来做更多的事情,例如:

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); 
    } 
} 

各种决定添加到您的工厂:

public class MyClassFactory 
{ 
    private static List<Tuple<IFactoryRule, Func<IMyClass>>> _rules = new List(); 

    static MyClassFactory() 
    { 
     // rules are evaluated in this same order 
     _rules.Add(new SimpleRule(1,2,3),() => new Simple()); 
     _rules.Add(new ComplexRule(p => p.a + p.b == p.c),() => new Complex()); 
    } 

    public static IMyClass Create(PropertySet pset) 
    { 
     if (pset == null) 
      throw new ArgumentNullException("pset"); 

     // try to find a match 
     Tuple<IFactoryRule, Func<IMyClass>> rule = 
      _rules.FirstOrDefault(r => r.First.CanInstantiate(pset)); 

     if (rule == null) 
      throw new ArgumentException(
       "Unsupported property set: " + pset.ToString()); 

     // invoke constructor delegate 
     return rule.Second(); 
    } 
} 

[编辑:加入MyClassFactory.Create方法]

正如你可以看到,有一个在该溶液中的规则没有散列映射,因此规则列表中Create方法进行评价逐个(FirstOrDefault将遍历该列表直到找到第一场比赛)。

如果您有很多规则(超过20个),并且您正在实例化一百万个对象,您会注意到与HashSet解决方案相比的速度差异(但这两种方法实际上无法进行比较,因为哈希集只能做平等比较)。

其他那是,用法类似于Andrew's solution

IMyClass instance = MyClassFactory.Create(propSet); 
+0

我喜欢这个..你能提供一个你如何称呼它的例子吗? – MalcomTucker 2011-04-28 17:33:55

1

你可以把它转换成一个从属性元组(映射到一个类中)到类名的映射,然后你可以使用反射来实例化。或者,也可以是地图层次结构。在Java中,我将使用Spring在XML配置文件中定义这样的映射;在C#中也可能有类似的方法来实现这一点。

+0

我曾想过地图的层次结构,如果这是我能做的最好的事情,那就这样吧。这仍然有点笨拙的工作,虽然在嵌套开关肯定改善.. – MalcomTucker 2011-04-28 13:06:36

4

把这件作品放在一起。 :)

参数映射到匿名创建者。

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() 
} 
0

有很多选择,但这取决于如何缩放。这些条件还有什么其他的价值/逻辑?

一种选择是使用组合SWICH

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 ...; 
} 
+0

反射解决方案看起来像过度杀伤:) – 2011-04-28 13:13:45

+0

哇,这是非常讨厌的。 :)我更喜欢这个解决方案的地图(prop.tuples到工厂方法)。 – Groo 2011-04-28 13:17:36

+0

@Andrew Frolov,它涉及添加两行特殊处理代码。创建数据结构来反映Java为你做的事情是过度的。 ;) – 2011-04-28 13:29:35

2

根据3个属性的性质,您可以创建一个定义每个类的自定义属性。所以,你将不得不类,如:

[IMyClass(Prop1 = 1, Prop2 = 3, Prop3 = 4)] 
public class Blah : IMyClass 

[IMyClass(Prop1 = 4, Prop2 = 5, Prop3 = 6)] 
public class Blah2 : IMyClass 

然后在你的工厂方法,你可以通过IMyClass所有实现使用反射来循环,找回自己IMyClassAttribute情况,并检查属性相匹配。这使您不必将映射保留在两个地方,并且在类定义本身中包含类映射。

编辑仅供参考,这是基于C#,我不知道,如果Java有类似的功能或没有(虽然我敢肯定它)

0

我道歉,如果这是无益的,但我无法抗拒指出,这样的事情是很容易在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#中并不存在。

相关问题