这与以下问题有关:
How to improve the builder pattern?
我很好奇是否可以实现具有以下属性的构建器:
-
部分或全部参数是必需的
-
没有方法接收许多参数(即,没有提供给初始构建器工厂方法的默认值列表)
-
可以重新分配所有构建器字段任意次
-
编译器应检查是否已设置所有参数
-
可以要求参数最初按某种顺序设置,但是一旦设置了任何参数,所有后续构建器都可以再次设置此参数(即,您可以重新分配所需构建器的任何字段的值)
-
对于setter不存在重复的代码(例如,构建器子类型中没有重写的setter方法)
下面是一个失败的尝试(省略了空的私有构造函数) . 考虑以下玩具构建器实现,并注意带有“Foo f2”的行具有编译器错误,因为a的继承setter返回BuilderB,而不是BuilderFinal . 有没有办法使用java类型系统来参数化setter的返回类型以实现上述目标,或者以其他方式实现它们 .
public final class Foo {
public final int a;
public final int b;
public final int c;
private Foo(
int a,
int b,
int c) {
this.a = a;
this.b = b;
this.c = c;
}
public static BuilderA newBuilder() {
return new BuilderC();
}
public static class BuilderA {
private volatile int a;
public BuilderB a(int v) {
a = v;
return (BuilderB) this;
}
public int a() {
return a;
}
}
public static class BuilderB extends BuilderA {
private volatile int b;
public BuilderC b(int v) {
b = v;
return (BuilderC) this;
}
public int b() {
return b;
}
}
public static class BuilderC extends BuilderB {
private volatile int c;
public BuilderFinal c(int v) {
c = v;
return (BuilderFinal) this;
}
public int c() {
return c;
}
}
public static class BuilderFinal extends BuilderC {
public Foo build() {
return new Foo(
a(),
b(),
c());
}
}
public static void main(String[] args) {
Foo f1 = newBuilder().a(1).b(2).c(3).build();
Foo f2 = newBuilder().a(1).b(2).c(3).a(4).build();
}
}
5 回答
您的要求非常难,但请查看此通用解决方案是否符合要求:
据我所知,应该使用构建器模式,以防使用多个参数,这使得调用相当复杂,因为参数可能会交换位置或者没有明确说明哪个参数用于什么 .
一个经验法则是要求构建器的构造函数中的必需参数和方法中的可选参数 . 然而,通常可能需要超过4个参数,这使得再次调用相当不清楚并且模式是多余的 . 因此,也可以使用拆分为默认构造函数和每个参数的参数设置 .
检查应该在自己的方法中进行,该方法在构建方法中调用,因此您可以使用
super
调用它 . 编译时安全性仅在正确的数据类型上得到保证(只有异常 - 可以为null,必须在checkParameters()
-method中获取) . 但是,您可以强制在Builder构造函数中设置所有必需参数,但如前所述,这可能会导致冗余模式 .在从C和构建器扩展D时,您需要覆盖
checkParameters()
和build()
方法 . 由于使用泛型,正确的类型将在调用时返回build()
抽象构建器类非常简单:
例外也很简单:
最后但并非最不重要的主要方法:
输出:
虽然,在使用Generics之前,我建议坚持使用KISS策略并忘记构建器的继承并将它们编码为简单和愚蠢(其中一部分包括哑复制和粘贴)
@edit:好的,在完成所有的工作并重新阅读OP以及链接的帖子后,我对这些要求做了完全错误的假设 - 就像德国的措辞所说:“手术成功,病人已经死了” - 尽管我离开了这篇文章,以防万一有人想要一个复制和粘贴解决方案的构建器继承实际上返回正确的类型而不是基类型
我有一次a crazy idea,它有点违背你的一些要求,但我认为你可以让构建器构造函数获取所需的参数,但是这样可以清楚地知道正在设置哪些参数 . 看一看:
对于其他客户,静态导入是您的朋友:
基于Jordão的想法,我提出了以下内容,即使类型参数中存在一些重复的代码,也可以满足所有要求1-6 . 本质上,我们的想法是通过使用类型参数覆盖继承方法的返回值来“传递”每个方法的返回类型 . 即使代码是冗长的和不切实际的,而实际上需要欧米茄(N ^ 3)字符,如果你出去扩展它来场n的任意数,我张贴,因为我认为这是一个有趣的使用Java类型系统 . 如果有人能找到减少类型参数数量的方法(特别是渐近),请在评论中发帖或写下另一个答案 .
为什么不想覆盖BuilderFinal中的setter?他们只需要转发超级方法: