天生器形式在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的簡便利用。
版權聲明:本文來自互聯網整理發布,如有侵權,聯系刪除
原文鏈接:http://www.freetextsend.comhttp://www.freetextsend.com/wangluozixun/55899.html