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

在我们的一些项目中,有一个类层次结构,它在向下传递的过程中添加了更多的参数。在底部,一些类最多可以有30个参数,其中28个参数正在被传递到超级构造函数中。

我承认通过类似 Guice 的东西使用自动化的 DI 会很好,但是由于一些技术原因,这些特定的项目受到 Java 的限制。

按字母顺序组织参数的惯例不起作用,因为如果一个类型被重构(你为参数2传入的圆圈现在是一个形状) ,它可能会突然失序。

这个问题可能是具体的,充满了“如果这是你的问题,你在设计水平上做错了”的批评,但我只是在寻找任何观点。

80045 次浏览

我所能想到的就是通过重构来减少参数的数量和继承层次结构的深度,因为没有什么能够真正帮助保持20多个参数的正确性。你只需要在查看文件的同时接听每一个电话。

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

最好的解决方案是在构造函数中不要有太多的参数。只有构造函数中真正需要的参数才是需要正确初始化对象的参数。您可以拥有具有多个参数的构造函数,但也可以拥有仅具有最小参数的构造函数。其他构造函数调用这个简单构造函数,然后调用 setter 设置其他参数。通过这种方式,您可以避免使用越来越多的参数的链式问题,而且还可以使用一些方便的构造函数。

As you are constrained to Java 1.4, if you want DI then Spring would be a very decent option. DI is only helpful in places where the constructor parameters are services or something that does not vary during runtime.

If you have all of those different constructors due to the fact that you want variable options on how to construct an object, you should seriously consider using the Builder pattern.

What you probably want to do is have a Builder class. Then you would do something like this:

MyObject obj = new MyObjectBuilder().setXxx(myXxx)
.setYyy(myYyy)
.setZzz(myZzz)
// ... etc.
.build();

请参阅 乔什 · 布洛赫的演讲(PDF)或 this review of Effective Java的第8页及下文

The Builder Design Pattern might help. Consider the following example

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

这样我们就可以编写像

Student s1 = new StudentBuilder().name("Eli").buildStudent();
Student s2 = new StudentBuilder()
.name("Spicoli")
.age(16)
.motto("Aloha, Mr Hand")
.buildStudent();

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

可以将相关参数封装在对象中吗?

例如,如果参数类似于


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;

那么你就可以:


MyClass(Address homeAddress, int foo, double bar) {
super(homeAddress);
this.foo = foo;
this.bar = bar;
}

使用构建器模式可能是 解决方案。

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

对于我过去的所有项目来说,有一次我得到的参数太多了(那是8而不是28!)我创建了一个更好的数据模型来清除代码。

在使用构建器模式时,我确实可以推荐使用 不可改变POJOBuilder