【问题标题】:Constructor requiring many input arguments需要许多输入参数的构造函数
【发布时间】:2015-04-17 21:04:24
【问题描述】:

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

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();

第二个变体看起来更优雅、更干净,但如果类使用不正确,则会出现更多错误。

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

【问题讨论】:

标签: java oop constructor


【解决方案1】:

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

请考虑在创建对象时并非所有变量都是已知的/可能为空。因此,与其将空值传递给构造函数,不如在没有参数的构造函数中实例化。例如

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

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

【讨论】:

    【解决方案2】:

    来源Effective Java

    构造函数的问题是它们不够灵活。我们的 示例只有 5 个参数。但是上课的时候想想现实世界 人可能有 20 个或更多的字段。在这种情况下,创建这样的 对象非常冗长。

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

    在这种情况下,您可能需要创建几个重载的 具有不同数量参数的构造函数。为避免重复 分配代码是使用所谓的伸缩式的常用技术 构造函数,即构造函数调用其他构造函数时的模式。 这种模式很好,但有些过于冗长且难以理解 修改。

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

    让我们举一个简单的 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 的可读性。

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • +1 Fluent Builder,尤其是对于不可变对象。对于 Builder,Object 本身应该只有一个带一个参数的受保护构造函数(builder 本身)。对象构造函数直接读取构建器属性,因此构建器或对象都不需要 getter。 (Builder 和 Object 已经紧密耦合,所以直接访问属性是有意义的)。
      【解决方案4】:

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

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

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

      【讨论】:

        【解决方案5】:

        如果真的有那么多参数,可能是你的类试图同时处理太多的东西。以此为暗示,它可以分解为更小的类。

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

        【讨论】:

        • 接受这个答案,因为它是最古老的并且建议了所有其他人的建议。
        【解决方案6】:

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

        有两种处理方法:

        1. 拥有一个 Context 类,它具有所有参数作为类属性。这样,您将只有一个构造函数参数。

        2. 使用构建器模式。创建一个构建器类。在 builder 类的构造函数中,调用类的构造函数,在 build 方法中调用 setter。此外,请确保将类的构造函数设为私有。

        【讨论】:

        • 我对 1 没有同样的问题吗?我只是在新的 Context 类中遇到了它?
        • 是的,这是真的。构建器模式是解决这个问题的方法。
        【解决方案7】:

        我取决于你认为班级基本上需要什么

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-02-26
          • 2020-01-27
          • 2021-11-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多