用Java管理带有许多参数的构造函数


105

在我们的某些项目中,存在一个类层次结构,该层次结构在链中沿链添加了更多参数。在底部,某些类最多可以具有30个参数,其中28个参数仅被传递给超级构造函数。

我将承认通过Guice之类的工具使用自动化DI会很好,但是由于某些技术原因,这些特定项目仅限于Java。

按类型按字母顺序组织参数的约定不起作用,因为如果重构类型(您为参数2传入的Circle现在是Shape),它可能会突然混乱。

这个问题可能是针对特定问题的,并且充满了“如果这是您的问题,那么您在设计级别上做错了”的批评,但是我只是在寻找任何观点。

Answers:


264

生成器设计模式可能会有所帮助。考虑以下示例

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构造函数引发异常。而且它使我们可以使用默认/可选参数,而无需跟踪任何类型的参数顺序,因为这些调用的任何顺序都可以很好地工作。


10
当然,对于静态导入,您甚至根本不需要“看到”这些“构建器”。例如,您可能具有返回生成器的静态方法name(String name)和返回学生的Student(StudentBuilder)。因此,Student(name(“ Joe”)。age(15).motto(“ I'm weve self”)));
oxbow_lakes

2
@oxbow_lakes:在您的示例中,哪个类具有静态方法名称(字符串名称)?
user443854

从技术上讲,可以使用“学生”类来建立新学生。我已经在Student类中添加了方法,并且效果很好。这样,我就不需要另一个构建器类。我不确定这是否可取。是否有理由使用另一个(StudentBuilder)类进行构建?
WVrock 2014年

1
@WVrock:这取决于您的实现。就像我在回答中说的那样,对学生班级本身进行操作可能会使班级处于半初始化状态,例如,如果您有一个尚未初始化的必填字段。
Eli Courtwright 2014年

@EliCourtwright我想这是关于首选项/代码设计的。我没有使构造函数引发异常,而是使buildStudent()方法引发了异常。
WVrock

24

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

例如,如果参数像


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



8

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

但是一旦您获得20到30个参数,我就会猜到这些参数之间有很高的关系。因此(如建议的那样)将它们包装成逻辑上合理的数据对象可能是最有意义的。这样,数据对象已经可以检查参数之间约束的有效性。

对于过去的所有项目,一旦我发现参数太多(即8个而不是28个!),便可以通过创建更好的数据模型来清理代码。


4

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

如果由于需要有关如何构造对象的变量选项而拥有所有这些不同的构造函数,则应认真考虑使用Builder模式。


正如您所提到的,参数主要是服务,因此DI是我所需要的。我认为在其他几个答案中提到的Builder模式正是我所希望的。
史蒂夫·阿姆斯特朗

4

最好的解决方案是在构造函数中不要包含太多参数。只有构造函数中真正需要的参数才是正确初始化对象所需的参数。您可以使构造函数具有多个参数,但也可以使构造函数具有最小参数。附加的构造函数调用此简单的构造函数,然后在setter之后设置其他参数。这样,您可以避免使用越来越多参数的链式问题,但也可以使用一些便利构造函数。



1

我几乎可以想到所有减少重构的参数以减少继承层次结构的参数数量和深度,因为实际上并没有什么办法可以使20多个参数保持一致。在查看文档时,您将只需要进行每个调用。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.