因此,在着名的 Effective Java 一书中,它引入了一个 Builder模式,您可以在其中使用内部静态Builder类来实例化一个类。这本书建议了以下类的设计:
public class Example {
private int a;
private int b;
public static class Builder() {
private int a;
private int b;
public Builder a(int a) {
this.a = a;
return this;
}
public Builder b(int b) {
this.b = b;
return this;
}
public Example build() {
return new Example(this);
}
}
private Example(Builder builder) {
this.a = builder.a;
this.b = builder.b;
}
}
但是我没理解为什么我们真的需要一个内部Builder class
?上面的代码有重复的字段声明行(int a,b),这会变得相对凌乱如果我们有更多的领域。
为什么不摆脱Builder
课程,让Example
课程采用set
课程中的所有Builder
方法?
因此要实例化Example
,它将变为Example e = new Example().a(3).b.(3);
而不是Example e = new Example.Builder.a(3).b(3).build();
注意:本书为具有较长参数列表的类建议了这种模式。
答案 0 :(得分:10)
Builder是构建复杂对象的模式。我不认为你的榜样很复杂;实际上,构建器添加了大量不必要的代码,而不仅仅是使用构造函数参数。
您想要使用构建器的原因有几个:
构造复杂的不可变对象。不可变对象需要具有最终(或逻辑上最终)字段,因此必须在构造时设置它们。
假设您有N个字段,但您只想在某些用例中明确设置其中的一些字段。您需要最多2 ^ N个构造函数来覆盖所有情况 - 称为“telescoping”,因为参数列表的长度变得越来越长。构建器允许您为可选参数建模:如果您不想设置该参数,请不要调用该setter方法。
允许自我记录参数的含义。通过适当地命名setter方法,您可以一目了然地看到值的含义。
它还有助于验证您是否意外地反转了相同类型的参数,因为您可以看到每个值的用途。
答案 1 :(得分:1)
如果外部类中的字段是final,那么如果要逐步指定参数值,则构建器是必需的,因为必须在构造函数中初始化所有字段。
构建器内部类允许以递增方式初始化字段。
正如其他人所指出的那样,这也适用于不可变对象。这些领域不需要是最终的;如果在外层没有提供制定者,它们将是有效的。
构建器可以比直接构造更有效地累积参数。考虑StringBuilder
。它分配一个临时缓冲区来累积部分结果。 "构建"在这种情况下的操作是toString()
。
最后,你可以在类的构造函数中做些事情。如果您需要将值传递给超级构造函数,但该值不是构造函数的参数的一部分,则可能无法实现,因为您必须先调用super()
,否则您可能无法在super(...)
调用中将参数创建为简单表达式。想到BoxLayout。您将JPanel
传递给BoxLayout
构造函数。您将布局传递给JPanel
构造函数。鸡肉和鸡蛋。并且不允许使用此代码,因为尚未构造this
。
class X extends JPanel {
X() {
super( new BoxLayout(this) ); // Error: Cannot use "this" yet
}
}
幸运的是,JPanel
不是一成不变的;你可以在施工后设置布局。
答案 2 :(得分:1)
理由是复杂的课程。请注意,Builder
对象返回自身,因此可以进行链接,例如:
Example exp = Example.Builder().a(5).b(10).build();
Apache在某些情况下使用此方法来允许增量设置各种值。它还允许.build()
方法检查所有正确的值,以便在需要时创建对象。