0

让我们假设一个类Foo有 2 个实例变量,int x 和 int y。用例要求我可以使用无参数、单个参数或所有参数构造类。

input X    input Y

  0         0           no constuctor

  0          1          public Foo(int x);

  1          0          public Foo(int y);

  1          1          public Foo(int x, int y);

现在在这种情况下,约定/最佳实践是什么。我需要添加构造函数的所有排列吗?如果是,它将导致指数增长。如果否,那么有什么解决方案?

4

5 回答 5

7

您最好的选择是使用构建器模式,如

new FooBuilder().build()   // Uses default values for both parameters

new FooBuilder().setX(42).build()  // Uses default value for y

new FooBuilder().setY(-13).build()  // Uses default value for x

new FooBuilder().setX(42).setY(-13).build()  // Supplied values for both

然后你的建造者可能看起来像

class FooBuilder {
  public Foo build() { return new Foo(...); }
  public FooBuilder setX(int x) { ...; return this; }
  public FooBuilder setY(int y) { ...; return this; }
}
于 2013-09-30T03:10:55.297 回答
2

简短的回答:这取决于。

更长的答案:添加与实际用例相对应的那些。如果这意味着一个带有所有参数,或者一个没有(设置参数的默认值),那就太好了。如果有一些常见的用例让一些参数默认,而另一些则没有,请为此提供构造函数。

请注意,您不能有多个具有相同顺序、数量和参数类型的构造函数。

于 2013-09-30T03:10:35.457 回答
1

这种情况的常用方法是使用构建器模式。

构建器的想法是您有一个中间对象,该对象了解各种必需或可选参数,并用于生成适当构造的对象。这意味着您只需要在您的实际业务对象中拥有全参数构造函数,这可以大大简化事情。

您可以通过执行以下操作来使用构建器:

Foo foo = new FooBuilder().withX(10).withY(25).build();

Project Lombok 提供了一个非常有用的@Builder注释,它将为您处理所有这些:有关详细信息,请参阅http://projectlombok.org/features/experimental/Builder.html

于 2013-09-30T03:11:50.700 回答
1

正如@Platinum Azure 所说,这取决于。既然你还没有说有一个占主导地位的用例......

请记住,作为类设计师,我们是为客户程序员设计的。他们的代码对第三方来说有多清晰?他们怎么知道的意思

Foo foo = new Foo();
Foo foo2 = new Foo(2);
Foo foo23 = new Foo(2,3);

你希望他们遵循什么风格的代码?“everythingononeline”风格:

Foo foo = new Foo().setNumHeads(2).setNumLegs(3);

其中,当有很多东西要设置时,可以适应

Foo foo = new Foo()
    .setNumHeads(2)
    .setNumLegs(3);

还是传统的OO风格:

Foo foo = new Foo();
foo.setNumHeads(2)
foo.setNumLegs(3);

那么,你现在需要做多少工作呢?你需要一个建造者,还是很Foo简单不需要(如上面的例子)?

另一种方法是提供默认值:

Foo foo = new Foo(Foo.DefaultNumHeads, Foo.DefaultNumLegs, Foo.DefaultNumTails);

int它适用于一些参数,但如果默认值定义为s,则当有很多参数时可能容易出错。因此,您可以应用命名参数习语的变体。这是一些建议构建器的地方,如Java 中的 Named Parameter idiom

Foo foo = new Foo(Foo.DefaultNumHeads, Foo.DefaultNumLegs, Foo.DefaultNumTails);

其中Foo.DefaultNumHeads等被定义为final对象。当你到达

Foo foo = new Foo(Foo.NumHeads(2), Foo.NumLegs(3), Foo.NumTails(5));

您正在Foo.

生态系统中常见的构造函数是什么风格Foo?如果其他类已经有类似的论点,那么也许也Foo应该如此。

关于组合爆炸的要点取决于可以预期的变化Foo。如果Foo是一个没有经过深思熟虑的抽象Foo我们就会得到一个组合爆炸,我们会一次又一次地返回并改变,违反了开放/封闭原则。不要这样做。

所以,这取决于。

于 2013-09-30T04:29:12.273 回答
0

有一个接受对象列表/数组的构造函数。在您的构造函数中迭代它并根据您的需要设置值。我假设您已经为班级中的所有可用字段设置了设置器。

优点:您不需要编写不同的构造函数

缺点:您需要知道并理解您在列表/数组中的索引或位置决定了要设置的字段。

Foo (Object[] args){
this.setX(args[0]); // have null check accordingly
this.setY(args[1]);    
}
于 2013-09-30T03:23:18.810 回答