【发布时间】:2017-11-01 12:19:06
【问题描述】:
我的理解是构建器模式的存在是为了避免多个重载的构造函数(对于比我的示例更复杂的类)
public class Example {
private String a,b,c;
public Example() {
//setup defaults
}
public Example(String a) {
this.a=a;
//setup defaults
}
public Example(String a, String b) {
this.a=a;
this.b=b;
//setup defaults
}
public Example(String a, String b, String c) {
this.a=a;
this.b=b;
this.c=c;
}
}
但是当切换到构建器时,以下哪种方法是正确的?
public class Example {
public static class Builder {
//accessors
public Example build() {
//we setup defaults through getters
//and example only has the 'full' constructor
return new Example(getA(), getB(), getC());
}
}
}
或
public class Example {
public static class Builder {
//accessors
public Example build() {
//pass in the builder and let 'Example' care about defaults
return new Example(this);
}
}
}
或
public class Example {
public static class Builder {
//accessors
public Example build() {
//only empty constructor exists which sets all defaults
//access fields directly to override defaults
Example e = new Example();
e.a = a;
e.b = b;
e.c = c;
return e;
}
}
}
这些是否打破了建造者模式? 是否有规范正确的方法?
(我想指出,Oracle 和 Google 的有关约定的文档均未涵盖此内容)
我知道有人问过this similar question,但据我所知(尽管有名字),这个问题只涉及实际的构建器模式与非构建器模式。
我更喜欢第三种方法,但我发现的许多示例都使用将构建器传递给构造器的方法。我不知道我是否错过了一些优势/潜在问题
【问题讨论】:
-
AFAIK,正确的方法是将所需的参数放入构造函数中,例如
new FooBuilder(required1, required2).bar(optional).build(); -
如您所知,主要思想是 build 方法将返回对象的完全构造的 valid 实例。在我看来,您应该在构建器中调用包装对象的私有构造函数,并将所有必需的参数作为参数传递给构造函数。我所遵循的方式如《Effective Java》一书中所示。
-
第一个和第二个基本是等价的。只传递构建器更简单,因为它避免了具有大量参数的构造器。它也是 Effective Java 中使用的一种(参见 informit.com/articles/article.aspx?p=1216151&seqNum=2)。第三个不允许使类不可变,这通常是使用构建器的主要原因。
-
由于构建的类是不可变的,它的字段都是
final,因此向类添加字段而不是构建器会由于未初始化的字段而产生编译错误。相反,将字段添加到构建器而不是构建的类会产生死代码警告。 -
不是对您问题的回答,而是对使用 Builder 模式的一个潜在有趣的反驳:Design Patterns and Anti-Patterns, Love and Hate
标签: java design-patterns builder anti-patterns