需要很多输入参数的构造函数

Constructor requiring many input arguments

我的问题涉及 java 中的构造函数。我了解到我们应该尽量减少方法的参数数量,包括构造函数。因此,如果我有一个 class 需要创建大量输入参数,我该如何最好地解决这个问题。我应该

A) 将它们全部包含在构造函数方法签名中:

示例:

public MyClass(SomeThing A, Vector v, OtherThing B, int number.. etc.){
      // construct
}

..

MyClass c= new MyClass(..);

c.doSomethingWithAllYouGot();

B) 我可以只构造很少的东西,然后要求对象用户在调用特定方法之前添加东西吗

public MyClass(SomeThing A){
      // construct
}

..

MyClass c=new MyClass(A);

c.attributeVector(v);
c.connectTo(B);
c.setNumber(n);
// etc.

c.doSomethingWithAllYouGot();

第二个变体看起来更优雅和干净,但如果 class 使用不当,会出现更多错误。

或者如果输入参数太多,class 的设计是否有问题?

如果真的有那么多参数,可能是您的 class 试图同时设置太多东西。以此为暗示,它可以分解成更小的 classes.

或者,您可以将构造函数包设为私有,并在同一个包中使用 builder 来实例化它。

我看你觉得class基本需要什么

如果 "SomeThing A" 是 class 离不开的东西 - 将其作为参数包含在主构造函数中 而 "Otherthing B" 如果不是基本面可以添加为 可以通过其他方法设置的默认值

在第二种方法中,如果在实例化 class 之后没有调用 setter 怎么办?您的实例将处于不一致状态。

有两种处理方法:

  1. 有一个上下文 class,它具有所有参数作为 class 属性。这样,您将只有一个构造函数参数。

  2. 使用建造者模式。创建一个生成器 class。在构建器 class 的构造函数中,调用您的 class 的构造函数,并在构建方法中调用设置器。另外,确保将 class 的构造函数设为私有。

老实说,有很多构造函数或带有很多参数的构造函数可能会变得一团糟。

您应该按照@TJ 的建议考虑构建器模式,但另一方面您应该考虑将构造函数中的参数减少到最小集合。这个集合可能是基本参数,可能是一段时间内不会改变的数据。

当然,如果所有参数都是必需的,并且必须将它们放入刚刚创建的对象中,那么您应该将它们放在构造函数中。

您建议的两种方法都可以,但都有一些缺点:

  1. 构造函数的参数列表太长,通常难以维护。例如,如果你期望传入5个字符串,用户不查阅文档几乎不可能知道传递什么。

  2. 它在很多情况下都有效,但如果内部状态一致性很重要,或者如果你想让你的对象不可变,这可能不是一个好主意。

还有其他常见的解决方案

Fluent builder,因此代码将如下所示:

MyClass c=new MyClassBuilder(A)
              .withAttributeVector(v);
              .connectTo(B);
              .withNumber(n)
              .create();

因此复杂的不直观的构造函数调用可以隐藏在构建器中。

使用对象作为输入参数:

public MyClass(MyClassConstructParam p){
}

..
MyClassConstructParam p=new MyClassConstructParam();
p.attributeVector(v);
p.connectTo(B);
p.setNumber(n);

MyClass c=new MyClass(p);

来源有效Java

The problem of constructors is that they are not flexible. Our example has only 5 parameters. But think about real world when class Person has probably 20 or more field. In this case creating of such object is pretty verbose.

在现实世界中,class 可以具有可选属性和强制属性

In this case you will probably want to create several overloaded constructors with different number of parameters. To avoid dupplicate assignemnt code it is common technique to use so called telescopic constructors, i.e. pattern when constructor invokes other constructor. This pattern is good but in some too verbose and hard for modifications.

现在另一种选择是 Bean Pattern 但它有其自身的缺点

让我们以 Simple Bean 为例

class Person {
   private String firstName;
   private String lastName;
   public String getFirstName() {return firstName;}
   public void setFirstName(String firstName) {this.firstName = firstName;}
   public String getLastName() {return lastName;}
   public void setLastName(String lastName) {this.lastName = lastName;}
}

您使用

创建实例
Person president = new Person();
p.setFirstName("George");
p.setLastName("Bush");

如您所见:

  1. 初始化确实分为 3 行。这意味着 当所有 3 行都完成并且在 之前状态不一致。
  2. 该对象确实是可变的:它调用的值可能会被更改 调用 setter.

这就是为什么 builder pattern 受到关注,它结合了 telescoping constructor 模式的安全性和 JavaBeans pattern.

的可读性

如果对象的变量都与它相关,那么它可以根据需要在构造函数中拥有尽可能多的变量。

考虑到创建对象时并非所有变量都known/may为空。因此,与其将空值传递给构造函数,不如在没有参数的情况下在构造函数中实例化。例如

public Student (String firstName, String lastName)
{
   this.firstName = firstName;
   this.lastName = lastName;
   this.grade = 0;
   this.report = "";
}

注意:当然,当您在实例化对象时确实知道这些值时,您也会有构造函数。