一饮笛,產(chǎn)生背景:屬性較多的對象蹄梢,一直分開設(shè)置比較麻煩;于是產(chǎn)生了builder方式生成對象壶冒。
1.1缕题,分開設(shè)置對象的屬性
如:
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;
}
}
如此定義對象,要new一個就會是這樣:
Student stu = new Student(1,hu,21);
or
Student stu1 = new Student(2,hu,21,1,shenzhen);
or
(如果你定義了set方法)
Student stu2 = new Student();
stu2.setAge(xxx);
stu2.setGender(1);
...
看上去似乎不是很優(yōu)雅胖腾,但是使用builder就可以鏈式設(shè)置對象參數(shù)
1.2烟零,builder鏈式設(shè)置對象參數(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;
}
// 自動生成toString方法快捷鍵,鼠標右鍵--》source--》generateToString
@Override
public String toString() {
return "Student [stuId=" + stuId + ", name=" + name + ", age=" + age + ", gender=" + gender + ", address="
+ address + "]";
}
public static class StudentBuilder {
// private final int stuId;//將Student的屬性用final修飾了,就一定需要先初始化
// 而且咸作,這里我們只提供锨阿,get方法獲得屬性值
// private final String name;//將Student的屬性用final修飾了
private int stuId;// 將Student的屬性用final修飾了
private String name;// 將Student的屬性用final修飾了
private int age;
private int gender;
private String address;
public StudentBuilder() {
}
public StudentBuilder(int stuId, String name) {
this.stuId = stuId;
this.name = name;
}
public StudentBuilder setAge(int age) {
// 將參數(shù)賦值給當前對象的屬性
this.age = age;
// 返回當前對象
return this;
}
public StudentBuilder setGender(int gender) {
// 將參數(shù)賦值給當前對象的屬性
this.gender = gender;
// 返回當前對象
return this;
}
public StudentBuilder setAddress(String address) {
this.address = address;
return this;
}
/**
* 最后需要一個build()方法,返回這個設(shè)置完參數(shù)的對象
*
* @return
*/
public Student build() {
// 返回新建的這個對象
return new Student(this);
}
}
}
使用時记罚,鏈式設(shè)置就行墅诡,記得最后來一個build(),構(gòu)建(返回)這么一個對象:
Student student1 = new Student.StudentBuilder(1, "小明")// 必填屬性在構(gòu)造方法中賦值
.setAge(1)// 設(shè)置可選屬性 年齡
.setGender(1)// 設(shè)置可選屬性 性別 默認1為男
.build();// 對象構(gòu)建完畢的標識桐智,返回Student對象
//沒有必設(shè)置的值
Student student2 = new Student.StudentBuilder().setAge(21).setGender(2).build();
二末早,進階
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);
}