在Java中管理具有许多参数的构造函数

Managing constructors with many parameters in Java

在我们的一些项目中,有一个类层次结构,当它沿着链向下时添加更多的参数。在底部,一些类可以有多达30个参数,其中28个参数刚刚被传递到超级构造函数中。

我承认使用Guice这样的自动化DI会很好,但是由于一些技术上的原因,这些特定的项目被限制在Java上。

按类型按字母顺序组织参数的约定不起作用,因为如果类型被重构(您为参数2传递的圆现在是一个形状),它可能会突然出现无序。

这个问题可能是具体的,充满了"如果这是你的问题,你在设计层面上做的不对"的批评,但我只是在寻找任何观点。


生成器设计模式可能会有所帮助。考虑下面的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class StudentBuilder
{
    private String _name;
    private int _age = 14;      // this has a default
    private String _motto =""; // most students don't have one

    public StudentBuilder() { }

    public Student buildStudent()
    {
        return new Student(_name, _age, _motto);
    }

    public StudentBuilder name(String _name)
    {
        this._name = _name;
        return this;
    }

    public StudentBuilder age(int _age)
    {
        this._age = _age;
        return this;
    }

    public StudentBuilder motto(String _motto)
    {
        this._motto = _motto;
        return this;
    }
}

这样我们就可以像

1
2
3
4
5
6
Student s1 = new StudentBuilder().name("Eli").buildStudent();
Student s2 = new StudentBuilder()
                 .name("Spicoli")
                 .age(16)
                 .motto("Aloha, Mr Hand")
                 .buildStudent();

如果我们去掉了一个必需的字段(假定名称是必需的),那么我们可以让学生构造函数抛出一个异常。它允许我们使用默认/可选参数,而不需要跟踪任何类型的参数顺序,因为这些调用的任何顺序都将同样有效。


你能在一个对象中封装相关参数吗?

例如,如果参数如下

1
2
3
4
5
6
<wyn>
MyClass(String house, String street, String town, String postcode, String country, int foo, double bar) {
  super(String house, String street, String town, String postcode, String country);
  this.foo = foo;
  this.bar = bar;
</wyn>

然后你可以有:

1
2
3
4
5
6
7
<wyn>
MyClass(Address homeAddress, int foo, double bar) {
  super(homeAddress);
  this.foo = foo;
  this.bar = bar;
}
</wyn>


您可能想做的是创建一个生成器类。然后你会这样做:

1
2
3
4
5
MyObject obj = new MyObjectBuilder().setXxx(myXxx)
                                    .setYyy(myYyy)
                                    .setZzz(myZzz)
                                    // ... etc.
                                    .build();

请参阅第8页和下面的Josh Bloch演示文稿(PDF),或对有效Java的回顾


好吧,使用构建器模式可能是一种解决方案。

但一旦你达到20到30个参数,我想参数之间有很高的关系。因此(如建议的那样)将它们包装成逻辑上健全的数据对象可能是最有意义的。这样,数据对象就可以检查参数之间约束的有效性。

对于我过去的所有项目,一旦我达到了有太多参数的地步(那是8而不是28!)我可以通过创建更好的数据模型来清理代码。


当您被限制为Java 1.4时,如果您想要DI,那么Spring将是一个非常不错的选择。DI仅在构造函数参数为服务或运行时不变化的地方有用。

如果您有所有这些不同的构造器,因为您需要关于如何构造对象的变量选项,那么您应该认真考虑使用构建器模式。


最好的解决方案是在构造函数中没有太多参数。构造函数中真正需要的参数是需要正确初始化对象的参数。可以有多个参数的构造函数,也可以有一个只有最小参数的构造函数。附加的构造函数调用这个简单的构造函数,并在该setter之后设置其他参数。这样可以避免使用越来越多的参数时出现链问题,但也有一些方便的构造函数。


我真的可以推荐在使用构建器模式时使用不可变项或pojobuilder。


重构以减少参数的数量和继承层次结构的深度几乎是我能想到的,因为没有什么能真正帮助保持20多个参数的正态性。在查看文档时,您只需要每次打电话。

您可以做的一件事是,将一些逻辑分组的参数分组到它们自己的更高级别对象中,但这有它自己的问题。