重构大型构造函数

时间:2011-08-31 11:02:07

标签: c# c#-4.0 refactoring

我们的域模型中有一些对象,你可以用它们来概括地称为 进攻性的 大型构造函数,这么大,以至于IntelliSense放弃了尝试向你展示它。 ..

提示一个包含50个左右参数的类型,主要是值类型,一些引用类型:

public class MyLegacyType
{
    public MyLegacyType(int a1, int a2, int a3, ... int a50) // etc
    {
    }
}

我现在就说,没有这种类型不能改变。类型本身在逻辑上代表一个实体,这恰好是属性重的。构造此类型的调用者提供来自多个源的大多数参数,但有些是默认的。也许有一种模式可以为建筑提供资源而不是结果。

然而,可以改变的是如何创建类型。目前,我们的代码部分受到以下影响:

一个直接的答案是利用默认值和命名参数的可选参数来帮助合并。我们在某种程度上对其他类型这样做,工作正常。

然而,感觉好像这是完全重构的一半。

另一个明显的解决方案是使用容器类型减少构造函数参数,这些容器类型具有曾经是构造函数参数的属性。这很好地整理了构造函数,并允许您在容器中嵌入默认值,但实际上将问题移到另一个类型上,并且可能与可选/命名参数用法相同。

还有Fluent构造函数的概念......在每个属性(WithIntAWithIntB)或容器类型(WithTheseInts(IntContainer c))的基础上。就个人而言,我喜欢这种来自呼叫方的方法,但是在大型类型上它又变得罗嗦,感觉好像我刚刚移动了一个问题而不是解决问题。

我的问题是,如果有一个被埋在这个烂摊子里,那么:这些问题的可行的重构策略是什么?请用一些相关的经验,陷阱或批评来解决一些问题。我倾向于Fluent的东西,因为我认为它看起来很酷,而且非常易读且易于合并。

我觉得好像我错过了构造函数重构的圣杯 - 所以我愿意接受建议。当然,这也可能只是一个令人遗憾和不可避免的副作用,因为首先拥有这么多属性的类型...

3 个答案:

答案 0 :(得分:12)

显然我们这里没有太多的背景,但是在50多个参数中,我的解释是这个课程做得太多,太复杂了。我将首先寻找将块拆分为更简单,更集中的类型的方法 - 然后将每个概念的封装到复合类中。所以它变成了:

public MyLegacyType(SomeConcept foo, AnotherConcept bar, ...)
{
}

只有概念之间编排所需的逻辑仍然存在于MyLegacyType中(任何特定于SomeConcept的逻辑都在那里等)。

这与您的“使用具有以前构造函数参数的属性的容器类型的reduce构造函数参数”不同,因为我们从根本上重构逻辑 - 而不仅仅是使用对象来替换构造函数参数。

答案 1 :(得分:4)

我会使用容器类型并使用C#4.0的即时属性分配。通过这种方式,可以轻松地在结果类型上使用Intellisense,同时仍然保留与原始类型的良好解耦。

例如:

public class MyLegacyType
{
    public MyLegacyType(MyConfiguration configuration) // etc
    {
      // ...
    }
}

public class MyConfiguration
{
   public int Value1 { get; set; }
   public int Value2 { get; set; }
   // ...
}

然后:

var myInstance = new MyLegacyType(new MyConfiguration
{
  Value1 = 123,
  Value2 = 456
});

答案 2 :(得分:1)

有一件事我不确定你的问题,这就是为什么你想在构造函数中使用所有这些参数?您是否正在使用构造函数代码中的所有参数?您对intellisense的问题可能来自于单个方法上的参数太多。在单一类型上具有许多字段/属性不会导致任何问题。

看起来你已经看到了一些管理args数量的方法,但是如果你能解释为什么你需要在构造函数中接收所有这些,我们可以在这个框之外思考。可能会有一些东西要看。