具有大参数的 Java 构造函数或 Java bean getter/setter 方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1632058/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 17:21:17  来源:igfitidea点击:

Java constructor with large arguments or Java bean getter/setter approach

javaconstructor

提问by deelo55

I can't decide which approach is better for creating objects with a large number of fields (10+) (all mandatory) the constructor approach of the getter/setter. Constructor at least you enforce that all the fields are set. Java Beans easier to see which variables are being set instead of a huge list. The builder pattern DOES NOT seem suitable here as all the fields are mandatory and the builder requires you put all mandatory parameters in the builder constructor.

我无法决定哪种方法更适合创建具有大量字段(10+)(所有必需的)getter/setter 的构造函数方法的对象。构造函数至少您强制设置所有字段。Java Beans 更容易查看正在设置的变量,而不是一个巨大的列表。构建器模式似乎不适合这里,因为所有字段都是必需的,并且构建器要求您将所有必需参数放入构建器构造函数中。

Thanks, D

感谢:D

回答by cletus

The better approach (imho) is to use some kind of builder:

更好的方法(恕我直言)是使用某种构建器:

MyClass a = new MyClassBuilder().blah("blah").foo("foo").doStuff().toMyClass();

where MyClass is still immutablebut has a far more readable creation than a constructor with 10 arguments.

其中 MyClass 仍然是不可变的,但比具有 10 个参数的构造函数更具可读性。

This is also called a fluent interface. Josh Bloch refers to this in Effective Java.

这也称为流畅的界面。Josh Bloch 在Effective Java 中提到了这一点。

回答by Brian Agnew

My first thought is to check whether your encapsulation model is correct. Having 10+ mandatory fields sounds like quite a lot and perhaps it makes more sense to have more finely-grained components in this scenario ?

我的第一个想法是检查您的封装模型是否正确。拥有 10 多个必填字段听起来很多,也许在这种情况下拥有更细粒度的组件更有意义?

Are some of these fields/parameters related ? Can they be combined into objects that make sense (e.g. x-coordinateand y-coordinatecombined into a Pointobject etc.)

这些字段/参数中的一些是否相关?它们可以组合成有意义的对象吗(例如x-coordinatey-coordinate组合成一个Point对象等)?

回答by Carl Smotricz

In his book Code Complete, Steve McConnell argues that no procedure should ever have more than a maximum of 6, maybe 7 arguments. Most of these statements are not just his opinion but backed by studies, e.g. of error rates related to code structure.

在他的Code Complete一书中,Steve McConnell 认为任何程序都不应超过最多 6 个,也许 7 个参数。大多数这些陈述不仅是他的观点,而且还得到了研究的支持,例如与代码结构相关的错误率。

Clean Codeby Robert Martin goes even further: He recommends 1 or 2 arguments, while 3 is already considered a "code smell". Personally, I think Clean Codeis a bit extreme in places, but on the whole it makes some good arguments.

Robert Martin 的Clean Code更进一步:他推荐了 1 或 2 个参数,而 3 个已经被认为是“代码异味”。就我个人而言,我认为Clean Code在某些地方有点极端,但总的来说,它提出了一些很好的论据。

"A whole bunch of parameters" (however many that may be) bespeaks a "kitchen sink" design with lots of afterthoughts and little structure. It also makes maintenance more difficult and error prone; at the very least, it makes code hard to read.

“一大堆参数”(无论可能有多少)表明“厨房水槽”设计有很多事后的想法和很少的结构。它还使维护更加困难且容易出错;至少,它使代码难以阅读。

All this makes for good reasons to think about cutting down on the number of parameters. Other answers have offered some practical suggestions.

所有这些都为考虑减少参数数量提供了充分的理由。其他答案提供了一些实用的建议。

回答by Yishai

I would recommend you consider the builder patternin such a case. You are guaranteed to get a valid object, without just having a huge list of parameters.

在这种情况下,我建议您考虑构建器模式。您可以保证获得一个有效的对象,而不仅仅是拥有大量参数。

The OP was update to reject the builder pattern, but it seems to be based on a misunderstanding. The fact that the Builder pattern exists does not remove the enforcement of all the parameters.

OP 是更新以拒绝构建器模式,但这似乎是基于误解。Builder 模式存在的事实并没有消除所有参数的强制执行。

Consider the following object:

考虑以下对象:

 public class SomeImmutableObject {
      private String requiredParam1;
      private String requiredParam2;
      //etc.

      private SomeImmutableObject() { //cannot be instantiated outside the class }

      public static class Builder {
          private SomeImmutableObject instance;
          public Builder() { instance = new SomeImmutableObject();
          public Builder setParameter1(String value) {
               instance.requiredParam1 = value;
               return this;
          }
          //etc for each parameter.

          public SomeImmutableObject build() {
             if (instance.requiredParam1 == null || instance.requiredParam2 == null /*etc*/)
                throw new IllegalStateException("All required parameters were not supplied.");
             return instance;
          }
      } 
 }

Note that you can accomplish basically the same thing by making the fields package private and putting the builder in the same package.

请注意,您可以通过将 fields 包设为私有并将构建器放在同一个包中来完成基本相同的事情。

And if for some reason you can't do that, you can still have the constructor with the 10 parameters, and then have the Builder be the only thing that calls that constructor, so that it is an easier API to use.

如果由于某种原因你不能这样做,你仍然可以使用带有 10 个参数的构造函数,然后让 Builder 成为唯一调用该构造函数的东西,这样它就成为一个更容易使用的 API。

So for all stated requirements, the Builder pattern works just fine. The fact that all 10 parameters are required does not disqualify the Builder pattern at all. If there is some other need that the pattern doesn't satisfy, please elaborate.

所以对于所有陈述的要求,建造者模式工作得很好。需要所有 10 个参数这一事实根本不取消 Builder 模式的资格。如果有其他模式不能满足的需求,请详细说明。

Edit: The OP added a comment (quite a while ago, but I just got an upvote on this question so I only saw it now) with an interesting question: How do you validate a primitive at a later point in time?

编辑:OP 添加了一条评论(很久以前,但我刚刚对这个问题投了赞成票,所以我现在才看到),并提出了一个有趣的问题:您如何在稍后的时间点验证原语?

There are a few ways around that problem, including a guarding boolean, but my preferred way would be to use a Double object like so:

有几种方法可以解决这个问题,包括一个保护布尔值,但我更喜欢的方法是使用像这样的 Double 对象:

     private Double doubleForPrimitive;

     public Builder setDouble(double d) {
         doubleForPrimitive = d;
     }

     public SomeImmutableObject build() {
         if(doubleForPrimitive != null) {
               instance.doubleParam = doubleForPrimitive;
         } else {
                throw new IllegalArgumentExcepion("The parameter double was not provided");
         }
         //etc.
     }

It should be noted that if you need true thread-safe immutability having all of the fields of the immutable object as final, this requires more boilerplate (storing the variables inside the builder and passing them to a private constructor of the immutable object), but the approach is still clean from the point of view of the client code.

应该注意的是,如果您需要真正的线程安全不变性,将不可变对象的所有字段作为最终对象,这需要更多样板(将变量存储在构建器中并将它们传递给不可变对象的私有构造函数),但是从客户端代码的角度来看,该方法仍然是干净的。

回答by Spike Williams

You might consider using a builder pattern, with the builder ensuring that all the fields are at least set to sensible defaults. Refer to the link for implementation, but you would wind up with a call that looks something like:

您可以考虑使用构建器模式,构建器确保所有字段至少设置为合理的默认值。请参阅实现链接,但您最终会得到一个类似于以下内容的调用:

Widget widge = new Widget.Builder(). manufacturer("333").serialNumber("54321").build();

回答by JuanZe

This two patterns are useful to think about this kind of scenario:

这两种模式对于考虑这种场景很有用:

回答by Erick Robertson

Ten arguments for a constructor is a lot. I would seriously think about if they are all required and some of them wouldn't make sense to combine into logical objects. If there truly are ten distinct required pieces of data, then the constructor should contain ten fields.

构造函数的十个参数很多。我会认真考虑它们是否都是必需的,并且其中一些组合成逻辑对象没有意义。如果确实有十个不同的必需数据,那么构造函数应该包含十个字段。

回答by JRL

IMHO, you should pass everything that is needed for an object to be valid according to your business logic in the constructor.

恕我直言,您应该根据构造函数中的业务逻辑传递对象有效所需的一切。

If the argument list is lengthy, you could create an object that contains the arguments and pass that.

如果参数列表很长,您可以创建一个包含参数的对象并传递它。

回答by Roland Illig

I would implement the builder pattern like this:

我会像这样实现构建器模式:

package so1632058;

public class MyClass {
  private final String param1;
  private final String param2;

  MyClass(Builder builder) {
    this.param1 = builder.param1;
    this.param2 = builder.param2;
  }

  public String getParam1() {
    return param1;
  }

  public String getParam2() {
    return param2;
  }

  @SuppressWarnings("hiding")
  public static final class Builder {
    String param1;
    String param2;

    public Builder param1(String param1) {
      this.param1 = param1;
      return this;
    }

    public Builder param2(String param2) {
      this.param2 = param2;
      return this;
    }

    public MyClass toMyClass() {
      return new MyClass(this);
    }
  }
}

And then have the following code to use it:

然后有以下代码来使用它:

package so1632058;

public class Main {

  public static void main(String[] args) {
    MyClass.Builder builder = new MyClass.Builder();
    builder.param1("p1").param2("p2");
    MyClass instance = builder.toMyClass();
    instance.toString();
  }

}

Some notes:

一些注意事项:

  • There are no methods with many parameters.
  • The additional checking can be done in the MyClassconstructor.
  • I made the constructor's visibility package-wide to avoid the "synthetic-access" warning.
  • Same for the builder's instance fields.
  • The only way to construct an instance of MyClassis via the Builder.
  • 没有带有很多参数的方法。
  • 额外的检查可以在MyClass构造函数中完成。
  • 我将构造函数的可见性设置为包范围以避免“综合访问”警告。
  • 构建器的实例字段也是如此。
  • 构造 的实例的唯一方法MyClass是通过Builder.

回答by ndp

This is pretty hard to answer in the abstract. What reallyneeds to be done is to look at those ten parameters and see what they are. I see these as the key questions:

这很难抽象地回答。什么真正需要做的是看那些十个参数,看看它们是什么。我认为这些是关键问题:

  • Can some of them be combined into higher level "value" objects? For example, variables X and Y can be combined into Point. We had lots of situations like this within a cargo routing program where all fields were modeled as primitive strings. Introducing a few higher level concepts really helped make it readable.
  • Can some of the parameters be "defaulted" to certain values?
  • Are they really all independent, orthogonal concepts? I've worked on lots of systems and never seen this to be true. If not, there is some thinking to do about these.
  • 它们中的一些可以组合成更高级别的“价值”对象吗?例如,变量 X 和 Y 可以组合成 Point。我们在货物路由程序中有很多这样的情况,其中所有字段都被建模为原始字符串。引入一些更高级别的概念确实有助于使其可读。
  • 某些参数可以“默认”为某些值吗?
  • 它们真的都是独立的、正交的概念吗?我曾在许多系统上工作过,但从未见过这是真的。如果没有,就需要考虑这些。