天生器形式在Java使用步骤中十分盛行。但它常常被各位曲解和错误地使用,从而招致运转时错误。
让我们记取使用Builder的目标:仅在某些目标中设置必要的字段,并将其他字段设置为默许值。比如,假如我们正在准备设置目标,那么仅变动必要的参数并将其他参数设置为默许值会很便利。
很多开发职员只选择了Builder形式的一局部:可以单独设置字段。第二局部:其他字段存在公道的默许值-通常会被忽略。
后果,很容易取得不完备的(局部初始化的)POJO。为了缓解此成绩,我们对build()办法举行了反省,最初约莫本人都没发觉啥成绩但就是出错。此时如今,主要的侵害以前形成:反省转移到了运转时间上。为确保统统正常,我们必要添加自用测试以掩盖创建POJO的代码中的一切实行途径。
起首,让我们界说目标。这里的目标是将反省前往到编译时。假如未构建完备POJO的代码无法经过编译,则将不必要自用测试,也无需在build()办法中实行反省。但最紧张的是,平常我们事情起来就更轻松。有更多时间摸鱼了。
那么,怎样做到这一点呢?最分明的办法是使用Fluent API形式。Fluent API有两个局部(特地说一句,就像Builder一样):提供一种便利的办法来调用链中的办法(这两个局部Fluent API和Builder都相反),并将链中的每个后续调用都限定为仅允许的办法集。
第二局部是我们所要说的局部。经过限定在构建POJO的每个步调中可以调用的办法集,我们可以欺压实行特定的调用序列,并build()仅在设置了一切字段时才启用对办法的调用。如此,我们将一切反省移回编译时间。作为便利的反作用,还确保构建特定POJO的一切地点看起来都相反。如此,发觉错误转达的参数或比力代码修订之间的变动将愈加容易。
为了区分传统的Builder和带有Fluent API的Builder,我将后者称为Fluent Builder。
假定我们要为如下所示的简便bean创建Fluent Builder:
public class SimpleBean {
private final int index;
private final String name;
public SimpleBean(final int index, final String name) {
this.index = index;
this.name = name;
}
public int index() {
return index;
}
public String name() {
return name;
}
}
在此示例中,我使用了Java 纪录获取器的称呼商定。在Java 14中,此类可以声明为纪录,因此必要的样板代码将大大变小。
让我们添加一个构建器。第一步很传统:
...
public static SimpleBeanBuilder builder() {
return new SimpleBeanBuilder();
}
...
让我们起首完成一个传统的天生器,如此会愈加清晰Fluent Builder代码是怎样派生的。传统的Builder类如下所示:
...
private static class SimpleBeanBuilder {
private int index;
private String name;
public SimpleBeanBuilder setIndex(final int index) {
this.index = index;
return this;
}
public SimpleBeanBuilder setName(final String name) {
this.name = name;
return this;
}
public SimpleBean build() {
return new SimpleBean(index, name);
}
}
...
一个紧张的察看:每个setter前往此值,这又允许此调用的用户调用builder中可用的每个办法。这是成绩的本源,由于build()在设置一切必要字段之前,允许用户过早调用该办法。
为了制造Fluent Builder,我们必要将约莫的选择限定为仅允许的选择,因此必需准确使用Builder。由于我们正在思索必要设置一切字段的情况,因此在每个构建步调中,仅有一种办法可用。为此,我们可以前往自用接口,而不是this让Builder完成一切这些接口:
...
public static SimpleBeanBuilder0 builder() {
return new SimpleBeanBuilder();
}
...
private static class SimpleBeanBuilder implements SimpleBeanBuilder0,
SimpleBeanBuilder1,
SimpleBeanBuilder2 {
private int index;
private String name;
public SimpleBeanBuilder1 setIndex(final int index) {
this.index = index;
return this;
}
public SimpleBeanBuilder2 setName(final String name) {
this.name = name;
return this;
}
public SimpleBean build() {
return new SimpleBean(index, name);
}
public interface SimpleBeanBuilder0 {
SimpleBeanBuilder1 setIndex(final int index);
}
public interface SimpleBeanBuilder1 {
SimpleBeanBuilder2 setName(final String name);
}
public interface SimpleBeanBuilder2 {
SimpleBean build();
}
}
这模板有点丑,换个办法。
第一步是中止完成接口,而是前往完成这些接口的匿名类:
...
public static SimpleBeanBuilder builder() {
return new SimpleBeanBuilder();
}
...
private static class SimpleBeanBuilder {
public SimpleBeanBuilder1 setIndex(int index) {
return new SimpleBeanBuilder1() {
public SimpleBeanBuilder2 setName(final String name) {
return new SimpleBeanBuilder2() {
public SimpleBean build() {
return new SimpleBean(index, name);
}
};
}
};
}
public interface SimpleBeanBuilder1 {
SimpleBeanBuilder2 setName(final String name);
}
public interface SimpleBeanBuilder2 {
SimpleBean build();
}
}
如此很多了。我们可以再次宁静地SimpleBeanBuilder从该builder()办法前往,由于此类仅公开一个办法,并且不允许用户过早构建实例。但更紧张的是,我们可以省略构建器中的整个设置器和可变字段样板,从而大大变小了代码量。这是约莫的,由于我们在可见和可拜候一切设置器参数的范围内创建了匿名类。
就代码总数而言,天生的代码与原始Builder完成相当。
但这还不是全部。由于一切匿名类实践上都是仅包含一种办法的接口的完成,因此我们可以用lambda交换匿名类:
private static class SimpleBeanBuilder {
public SimpleBeanBuilder1 setIndex(int index) {
return name -> () -> new SimpleBean(index, name);
}
public interface SimpleBeanBuilder1 {
SimpleBeanBuilder2 setName(final String name);
}
public interface SimpleBeanBuilder2 {
SimpleBean build();
}
}
注意,剩余的SimpleBeanBuilder类与其他构建器接口十分相似,因此也可以将其交换为lambda:
public static SimpleBeanBuilder builder() {
return index -> name -> () -> new SimpleBean(index, name);
}
public interface SimpleBeanBuilder {
SimpleBeanBuilder1 setIndex(int index);
}
public interface SimpleBeanBuilder1 {
SimpleBeanBuilder2 setName(final String name);
}
public interface SimpleBeanBuilder2 {
SimpleBean build();
}
最初:
底下是SimpleBean使用一切这些变动之后的完备代码:
public class SimpleBean {
private final int index;
private final String name;
private SimpleBean(final int index, final String name) {
this.index = index;
this.name = name;
}
public int index() {
return index;
}
public String name() {
return name;
}
public static Builder builder() {
return index -> name -> new SimpleBean(index, name);
}
public interface Builder {
Stage1 index(int index);
interface Stage1 {
SimpleBean name(final String name);
}
}
}
实践上实行少数利用的代码很少,大大多完成是一堆接口声明。添加,变动或删除字段十分简便,由于触及的代码很少。
关于尚未习气使用深层嵌套lambda的用户,此代码乍一看约莫会比力困难,但这是履历成绩。别的,无需手动编写此类代码,由于我们可以将此职责卸载到IDE(就像我们使用传统构建器一样)。
使用上述办法,我们可以用Fluent Builders代替传统的Builders,并经过Fluent API形式宁静性取得Builder的简便利用。
版权声明:本文来自互联网整理发布,如有侵权,联系删除
原文链接:https://www.yigezhs.comhttps://www.yigezhs.com/wangluozixun/55899.html