通常狀況咱們不會遇到這樣的狀況,使用靜態工廠方法,或者構造方法就足夠。可是它們也有一個限制就是,它們不能很好的擴展到不少可選參數的場景。隨着咱們業務的深刻,某些java bean 中的參數將會愈來愈多,咱們添加的構造方法也相應的增長。想一想一個10個參數的構造方法,我胃痛。java
既然想要用builder模式,咱們首先須要知道傳統方法的不足。程序員
咱們都用代碼實例來講話安全
public class Student {
private final String name; // required
private final String sex; // required
private final int weight; // optional
private final int height; // optional
private final int age; // optional
public Student(String name, String sex) {
this(name, sex, 0);
}
public Student(String name, String sex, int w) {
this(name, sex, w, 0);
}
public Student(String name, String sex, int w, int h) {
this(name, sex, w, h, 0);
}
public Student(String name, String sex, int w, int h, int a) {
this.name = name;
this.sex = sex;
this.weight = w;
this.height = h;
this.age = a;
}
}
複製代碼
當咱們想建立一個Student實例的時候,能夠設置全部參數的最短的夠着方法以下:bash
Student student = new Student("小明", "男", 50, 150, 16);
微信
一般狀況下,這個構造方法可能須要許多你不想設置的參數(是否是有點不爽),隨着參數的不斷增長,它很快會失控的。(你也會發瘋的)。首先咱們很難讀懂這個方法,其次若是反轉了兩個參數,編譯器是不會報錯的。好比身高和體重填反了,糟心。。。。。性能
當在構造方法中遇到許多可選參數時,另外一種選擇是 JavaBeans 模式,在這種模式中,調用一個無參數的構造函 數來建立對象,而後調用 setter 方法來設置每一個必需的參數和可選參數:ui
public class Student {
private String name; // required
private String sex; // required
private int weight; // optional
private int height; // optional
private int age; // optional
public Student() {}
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
public void setWeight(int weight) {
this.weight = weight;
}
public void setHeight(int height) {
this.height = height;
}
public void setAge(int age) {
this.age = age;
}
}
複製代碼
這種模式沒有伸縮構造方法的缺點。有點冗長,但建立實例容易,而且容易閱讀this
Student student = new Student();
student.setName("小明");
student.setSex("男");
student.setAge(16);
student.setWeight(50);
student.setHeight(150);
複製代碼
JavaBeans 模式自己有嚴重的缺陷。因爲構造方法在屢次調用中被分割,因此在構造過程當中 JavaBean 可能處於不一致的狀態。該類沒有經過檢查構造參數參數的有效性來執行一致性的選項。在不一致的狀態下嘗試使用 對象可能會致使與包含 bug 的代碼截然不同的錯誤,所以很難調試。一個相關的缺點是,JavaBeans 模式排除了讓類 不可變的可能性,而且須要在程序員的部分增長工做以確保線程安全spa
builder 模式結合了可伸縮構造方法模式的安全性和JavaBean模式的可讀性。線程
public class Student {
private final String name;
private final String sex;
private final int weight;
private final int height;
private final int age;
private Student(Builder builder) {
this.name = builder.name;
this.sex = builder.sex;
this.weight = builder.weight;
this.height = builder.height;
this.age = builder.age;
}
public static class Builder {
private final String name; // required
private final String sex; // required
private int weight; // optional
private int height; // optional
private int age; // optional
public Builder(String name, String sex) {
this.name = name;
this.sex = sex;
}
public Builder setWeight(int weight) {
this.weight = weight;
return this;
}
public Builder setHeight(int height) {
this.height = height;
return this;
}
public Builder setAge(int age) {
this.age = age;
return this;
}
public Student build() {
return new Student(this);
}
}
}
複製代碼
我首先來噴一下,代碼量翻倍了。可是Student 類是不可變的,全部的參數默認值都在一個地方。builder 的 setter 方法返回 builder 自己, 這樣調用就能夠被連接起來,從而生成一個流暢的 API。實例以下:
Student student = new Student.Builder("小明", "男").setWeight(50)
.setHeight(150).setAge(16).build();
複製代碼
代碼很容易編寫,更重要的是易於閱讀。 Builder 模式模擬 Python 和 Scala 中的命名可選參數。 這裏沒有涉及到有效性的檢查
builder 對構造方法的一個微小的優點是,builder 能夠有多個可變參數,由於每一個參數都是在它本身的方法中指 定的。
Builder 模式很是靈活。 單個 builder 能夠重複使用來構建多個對象。 builder 的參數能夠在構建方法的調用之間 進行調整,以改變建立的對象。 builder 能夠在建立對象時自動填充一些屬性,例如每次建立對象時增長的序列號。
Builder 模式也有缺點。爲了建立對象,首先必須建立它的 builder。雖然建立這個 builder 的成本在實踐中不太可 能被注意到,但在性能關鍵的狀況下可能會出現問題。並且,builder 模式比伸縮構造方法模式更冗長,所以只有在 有足夠的參數時才值得使用它,好比四個或更多。可是請記住,若是但願在未來添加更多的參數。可是,若是從構造 方法或靜態工廠開始,並切換到 builder,當類演化到參數數量失控的時候,過期的構造方法或靜態工廠就會面臨尷 尬的處境。所以,因此,最好從一開始就建立一個 builder。
總而言之,當設計類的構造方法或靜態工廠的參數超過幾個時,Builder 模式是一個不錯的選擇,特別是若是許 多參數是可選的或相同類型的。客戶端代碼比使用伸縮構造方法(telescoping constructors)更容易讀寫,而且 builder 比 JavaBeans 更安全。
ps:若是有微信讀書的書友,能夠來微信讀書羣傳送門找組織。