1 定義
建造者模式(Builder Pattern)屬於創建型設計模式。又有人稱爲創建者模式、生成器模式、構造器模式等,它主要用於複雜對象的創建。那什麼是複雜對象呢?其實簡單地說就是類中構造方法有多個重載的版本和最終構造方法參數特別多的類的對象或者構造方法少但是屬性特別多的類的對象。因爲這種類需要外部傳入相當多的參數來決定類的最終表現,所以它是複雜的。使用者在創建對象時需要決定使用哪個構造方法,然而裏面的參數相當多還得搞清楚這些參數的含義,或者構造方法簡單但需要多行代碼來設置相當多的屬性才能完成最終想要的對象效果。這種複雜的類讓使用者使用起來相當麻煩。然而建告者模式就是爲了解決這類情況下的痛點而出現的。
2 實現方式
假設當前有一個用戶類User,它內部擁有兩個必須字段mFirstName和mLastName,以及三個可選字段mSex、mAge和mTelephone。如果按常規思路實現該類可以有摺疊構造函數模式或者JavaBean模式。
2.1 摺疊構造函數方式
public class User {
private String mFirstName; // 必須
private String mLastName; // 必須
private boolean mSex; // 可選
private int mAge; // 可選
private String mTelephone; // 可選
public User(String firstName, String lastName) {
this(firstName, lastName, true, 0, null);
}
public User(String firstName, String lastName, boolean sex) {
this(firstName, lastName, sex, 0, null);
}
public User(String firstName, String lastName, int age) {
this(firstName, lastName, true, age, null);
}
public User(String firstName, String lastName, String telephone) {
this(firstName, lastName, true, 0, telephone);
}
public User(String firstName, String lastName, boolean sex, int age) {
this(firstName, lastName, sex, age, null);
}
public User(String firstName, String lastName, boolean sex, String telephone) {
this(firstName, lastName, sex, 0, telephone);
}
public User(String firstName, String lastName, int age, String telephone) {
this(firstName, lastName, true, age, telephone);
}
public User(String firstName, String lastName, boolean sex, int age, String telephone) {
mFirstName = firstName;
mLastName = lastName;
mSex = sex;
mAge = age;
mTelephone = telephone;
}
}
該類因爲需要提供外部根據不同的參數達到不同的表現,所以定義了8個構造方法,其中可見每個構造方法中都有firstName和lastName字段,表示爲實例化時必須字段,從第2到第7個構造方法分別對應不同的參數組合出不一樣的表現,最終前面7構造方法會彙集到第8個構造方法中來完成字段的賦值。
所以在多個構造方法提供下,使用者在創建對象時需要決定使用哪個構造方法,然而裏面的參數相當多還得搞清楚這些參數的含義。如果增加字段的話,就要增加對應的構造方法,代碼將變的難以理解和維護。
2.2 JavaBean方式
public class User {
private final String mFirstName; // 必須
private final String mLastName; // 必須
private boolean mSex; // 可選
private int mAge; // 可選
private String mTelephone; // 可選
public User(String firstName, String lastName) {
mFirstName = firstName;
mLastName = lastName;
}
public void setSex(boolean sex) {
mSex = sex;
}
public void setAge(int age) {
mAge = age;
}
public void setTelephone(String telephone) {
mTelephone = telephone;
}
}
該類可選的字段通過set屬性的形式進行賦值,從而僅實現一個必須參數構造函數,但是這樣做使得調用方需要使用多行代碼才能創建出一個自己滿意的對象,在調用setX方法前,對象處於不完整狀態,而且也喪失了不可變對象的所有好處。
2.3 簡化版的建造者模式
public class User {
private final String mFirstName; // 必須
private final String mLastName; // 必須
private final boolean mSex; // 可選
private final int mAge; // 可選
private final String mTelephone; // 可選
private User(Builder builder) {
mFirstName = builder.firstName;
mLastName = builder.lastName;
mSex = builder.sex;
mAge = builder.age;
mTelephone = builder.telephone;
}
public static class Builder {
private final String firstName;
private final String lastName;
private boolean sex;
private int age;
private String telephone;
public Builder(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public Builder sex(boolean sex) {
this.sex = sex;
return this;
}
public Builder age(int age) {
this.age = age;
return this;
}
public Builder telephone(String telephone) {
this.telephone = telephone;
return this;
}
public User build() {
return new User(this);
}
}
}
// 調用
User user = new User.Builder("子", "雲心")
.sex(true)
.age(18)
.telephone("1234567890")
.build();
建造者模式需要類內部創建一個靜態內部類,如上述中的Builder,Builder中有一套跟User類對應的字段,而每個可選字段又對應一個同名的方法,這些方法都返回類本身類型Builder,最終在build方法中將自己this傳入給User的私有構造方法來new出一個User對象並返回。User構造方法接收Builder對象,再將Builder的每一個字段賦值給本身每一個final的字段來完成初始化。
2.3.1 小結
簡化版的建造者模式一般要建造的類的構造方法是私有的,這保證了調用者不可隨意直接創建實例,類中字段使用final修飾表示其不可變性。調用者使用流式句語一步一步地完成對象的創建可使代碼更易讀和易維護,另外無論什麼時候創建出來的對象都是保持不變和狀態完整的。一般地當一個類的構造方法參數超過4個,而且參數存在可選參數時,就可以考慮使用建造者模式。
2.4 傳統版的建造者模式
上面建造者模式是在Java中常見的一種簡化版的建造者模式,傳統版的建造者模式與其有一定的不同和增加了複雜度。傳統版的建造者模式包括4個角色,分別是:
- 要生成的對象的產品(Product),目標的複雜對象類,也就是上面示例中的User。
- 抽象建造者(Builder),用於定義了構建Product的抽象步驟,可以是抽象類或接口。
- 具體實現建造者(ConcreteBuilder),Builder的實現類。
- 指揮者(Director),指揮並構造一個使用Builder的對象。
產品類,使用了JavaBean的方式來定義:
public class User {
private final String mFirstName; // 必須
private final String mLastName; // 必須
private boolean mSex; // 可選
private int mAge; // 可選
private String mTelephone; // 可選
public User(String firstName, String lastName) {
mFirstName = firstName;
mLastName = lastName;
}
public void setSex(boolean sex) { mSex = sex; }
public void setAge(int age) { mAge = age; }
public void setTelephone(String telephone) { mTelephone = telephone; }
}
抽象建造者類和具體實現建造者類,對應着具體產品類中的set屬性定義出抽象方法和實現,在具體實現類中完成產品類的創建:
public abstract class UserBuilder {
public abstract void setSex();
public abstract void setAge();
public abstract void setTelephone();
public abstract User getUser();
}
public class ManUserBuidler extends UserBuilder {
private User mUser;
public ManUserBuidler(String firstName, String lastName) {
mUser = new User(firstName, lastName);
}
@Override
public void setSex() {
mUser.setSex(true);
}
@Override
public void setAge() {
mUser.setAge(18);
}
@Override
public void setTelephone() {
mUser.setTelephone("1234567890");
}
@Override
public User getUser() {
return mUser;
}
}
指揮者類,主要作用於隔離生產過程和調用者,而且負責控制產品的生產過程,它針對抽象建造者進行完整的屬性設置從而達到對象的完整性。
public class UserDirector {
public void makeUser(UserBuilder builder) {
builder.setSex();
builder.setAge();
builder.setTelephone();
}
}
調用者:
UserDirector director = new UserDirector();
UserBuilder builder = new ManUserBuidler("子", "雲心");
director.makeUser(builder);
User user = builder.getUser();
2.4.1 小總
傳統版的建造者模式會事先通過具體實現建造者的角色完成了產品的建造,如果需要不同的產品表現需要另外再定義一個新的具體建造者。好處在於建造代碼與表現相分離,調用者只需要指定要建造的類再通過指揮者就可以得到它們,而不需要關心具體建造過程和細節。也可以跟產品生產過程進行了隔離。不過缺點也較爲明顯,當產品需要新增字段時,那麼修改的地方較多。所以它適用於創建較爲穩定不會發生變化的複雜對象,在Java開發中一般建議使用簡化版的建造者模式。