原文:http://blog.csdn.net/nugongahou110 https://blog.csdn.net/nugongahou110/article/details/50395698
builder設(shè)計模式我們很常見,比如我們使用AlertDialog的時候就使用的builder設(shè)計模式,著名的Universal-Image-Loader的初始化配置也是使用的builder設(shè)計模式弄捕,那么他們?yōu)槭裁词褂胋uilder設(shè)計模式,什么情況下我們應(yīng)該考慮使用builder設(shè)計模式艰额,這是我們很多人的疑惑,即便是學(xué)會了也不知道什么時候應(yīng)該使用,這篇文章我將告訴大家在什么情況下應(yīng)該考慮使用builder設(shè)計模式买乃,如果不用的話會有什么壞處饲漾,用了會有什么好處蝙搔。
如果我們有一個類Student,他有很多的屬性考传,但是僅僅姓名和學(xué)號是必須賦值的吃型,其他的屬性都是可選項,比如像下面代碼中所示
public class Student {
private final int stuId;//必須
private final String name;//必須
private final int age;//可選
private final int gender;//可選
private final int address;//可選
...//還有很多可選屬性
}
那么我們怎么來創(chuàng)建一個Student對象呢僚楞?我們看到每個屬性都用final來修飾了勤晚,說明每個屬性都要在構(gòu)造方法中被初始化,我們又必須提供各種參數(shù)數(shù)量的構(gòu)造方法泉褐,我們看如下代碼
public class Student {
private final int stuId;//必須
private final String name;//必須
private final int age;//可選
private final int gender;//可選
private final String address;//可選
public Student(int stuId,String name){
this(stuId,name,0,1,"");
}
public Student(int stuId,String name,int age){
this(stuId,name,age,1,"");
}
public Student(int stuId,String name,int age,int gender){
this(stuId,name,age,gender,"");
}
public Student(int stuId,String name,int age,int gender,String address){
this.stuId = stuId;
this.name = name;
this.age = age;
this.gender = gender;
this.address = address;
}
}
這樣做確實可以解決我們的需求赐写,但這還是可選參數(shù)不多的情況,如果有很多可選參數(shù)兴枯,我們就必須要寫很多個構(gòu)造函數(shù)血淌,這將導(dǎo)致代碼的可讀性和維護性變差,更重要的是财剖,當(dāng)我們要用到這個類的時候會感覺無從下手悠夯,我到底應(yīng)該用哪個構(gòu)造方法呢?應(yīng)該用兩個參數(shù)的構(gòu)造方法還是用三個參數(shù)的呢躺坟?如果我用兩個參數(shù)的構(gòu)造方法沦补,那么可選參數(shù)的默認(rèn)值是多少?
更棘手的是咪橙,如果我只想給Student對象設(shè)置address屬性而不設(shè)置age和gender屬性的話怎么辦夕膀?我們顯然還得再繼續(xù)添加構(gòu)造方法,或者我們只能調(diào)用全參的構(gòu)造方法美侦,然后給age和gender屬性設(shè)置個默認(rèn)值产舞。
還有一點,我們看到stuId菠剩,age易猫,gender都是int類型的,那么我們在創(chuàng)建Student對象時具壮,哪一個int類型的對象代表stuId准颓,哪一個代表age哈蝇,這還進一步增加了使用成本。
那么我們還有沒有其他的辦法攘已?答案是有炮赦!我們可以只設(shè)置一個默認(rèn)的無參構(gòu)造方法,然后給每個屬性添加getter和setter方法样勃,代碼如下
public class Student {
private int stuId;//必須
private String name;//必須
private int age;//可選
private int gender;//可選
private String address;//可選
public Student(){
}
public int getStuId() {
return stuId;
}
public void setStuId(int stuId) {
this.stuId = stuId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getGender() {
return gender;
}
public void setGender(int gender) {
this.gender = gender;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
這種方法看上去可讀性和維護性比較好吠勘,當(dāng)我們使用這個類的時候只需要創(chuàng)建一個空的對象并且設(shè)置我們需要的屬性就可以了。比如這樣:
Student stu = new Student();
stu.setStuId(1);
stu.setName("小明");
stu.setAge(12);
這樣做有兩個問題峡眶,第一個問題是我們的stu對象沒有一個創(chuàng)建完畢的標(biāo)識看幼,上面的stu對象我們設(shè)置了三個屬性,但當(dāng)別人看到這段代碼時幌陕,他不確定這個stu對象是只需要這三個屬性還是當(dāng)時作者忘了寫完整,除非所有的屬性都給set上汽煮,別人才能確保你這個對象創(chuàng)建完畢搏熄;另一個問題是任何人都可以在我們創(chuàng)建好的基礎(chǔ)上繼續(xù)改變它,也就是繼續(xù)給它set新的屬性或者刪除某個已經(jīng)set的屬性暇赤,這就會使我們的stu對象具有可變性心例,這會引起潛在的風(fēng)險。
好在我們還有第三種方法鞋囊,那就是builder設(shè)計模式了止后。
public class Student {
private final int stuId;//必須
private final String name;//必須
private final int age;//可選
private final int gender;//可選
private final String address;//可選
private Student(StudentBuilder builder){
this.stuId = builder.stuId;
this.name = builder.name;
this.age = builder.age;
this.gender = builder.gender;
this.address = builder.address;
}
public int getStuId() {
return stuId;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public int getGender() {
return gender;
}
public String getAddress() {
return address;
}
public static class StudentBuilder{
private final int stuId;
private final String name;
private int age;
private int gender;
private String address;
public StudentBuilder(int stuId,String name){
this.stuId = stuId;
this.name = name;
}
public StudentBuilder setAge(int age){
this.age = age;
return this;
}
public StudentBuilder setGender(int gender){
this.gender = gender;
return this;
}
public StudentBuilder setAddress(String address){
this.address = address;
return this;
}
public Student build(){
return new Student(this);
}
}
}
值得注意的幾點:
1.Student的構(gòu)造方法是私有的,也就是說我們不能直接new出Student對象
2.我們又將Student的屬性用final修飾了溜腐,并且我們在構(gòu)造方法中都為他們進行了初始化操作译株,我們只提供了getter方法
3.使用builder模式構(gòu)造出來的對象有更好的可讀性,等下我們會看到
4.StudentBuilder的屬性中只給我們必須的屬性添加的final修飾挺益,所以我們必須在StudentBuilder的構(gòu)造方法中為他們初始化
使用builder設(shè)計模式完美的解決了方法一和方法二的不足歉糜,并且兼具他們的優(yōu)點:具有必填屬性和可選屬性的區(qū)分,更重要的是:可讀性很強望众。唯一的不足是我們要在StudentBuilder中重復(fù)的寫一遍Student中的屬性匪补。
好,現(xiàn)在我們來創(chuàng)建一個Student對象吧
public Student getStudent(){
return new Student.StudentBuilder(1,"小明")//必填屬性在構(gòu)造方法中賦值
.setAge(1)//設(shè)置可選屬性 年齡
.setGender(1)//設(shè)置可選屬性 性別 默認(rèn)1為男
.build();//對象構(gòu)建完畢的標(biāo)識烂翰,返回Student對象
}
非常優(yōu)雅有木有夯缺?他是一個鏈?zhǔn)降恼{(diào)用,我們可以1行代碼就搞定甘耿,更重要的是踊兜,他的可讀性非常強,而且通過build()我們可以很明確的告訴別人我們的Student已經(jīng)創(chuàng)建完畢棵里。
builder設(shè)計模式非常靈活润文,一個builder可以創(chuàng)建出各種各樣的對象姐呐,我們只需要在build()之前調(diào)用set方法來為我們的對象賦值。
builder模式另一個重要特性是:它可以對參數(shù)進行合法性驗證典蝌,如果我們傳入的參數(shù)無效曙砂,我們可以拋出一個IllegalStateException異常,但是我們在哪里進行參數(shù)合法性驗證也是有講究的:那就是在對象創(chuàng)建之后進行合法性驗證骏掀。我們修改StudentBuilder的build()方法
public Student build(){
Student student = new Student(this);
if (student.getAge()>120){
throw new IllegalStateException("年齡超出限制");
}
return student;
}
為什么要先創(chuàng)建對象鸠澈,再進行參數(shù)驗證?因為我們的StudentBuilder是線程不安全的截驮,如果我們先進行參數(shù)驗證后創(chuàng)建對象笑陈,那么創(chuàng)建對象的時候?qū)ο蟮膶傩钥赡芤呀?jīng)被其他線程改變了,例如下面的代碼就是錯誤的
/**
* 錯誤的
*/
public Student build(){
if (age>120){
throw new IllegalStateException("年齡超出限制");
}
return new Student(this);
}
最后總結(jié)一下builder設(shè)計模式:當(dāng)我們的類中有很多屬性的時候葵袭,更重要的是有很多可選屬性的時候涵妥,我們就可以使用builder設(shè)計模式,因為這樣不僅可以使我們的類使用起來很優(yōu)雅坡锡,而且還可以給我們的對象一個創(chuàng)建完成的標(biāo)識蓬网,即build()方法。
作者:阿拉燈神燈
來源:CSDN
原文:https://blog.csdn.net/nugongahou110/article/details/50395698
版權(quán)聲明:本文為博主原創(chuàng)文章鹉勒,轉(zhuǎn)載請附上博文鏈接帆锋!