Java中的Builder模式
作為一只小白逊桦,在看到下面這樣額的代碼時,還真是一臉懵逼卜高。弥姻。。
new IncomingGarmentStatBuilder()
.id(UUID.randomUUID())
.endDateInRange(toDate)
.range(DAILY)
.org(organization)
.value(sumValueByOrg(dailyOnceStats, organization))
.build()
雖然可以猜到是在創(chuàng)建新對象掺涛,但是這種方式還真是詭異呢??
再加上當這段代碼混在java 8 的stream處理過程中時,像下面這樣薪缆,
List<IncomingGarmentStat> dailyStats = organizations.stream()
.map(organization ->
new IncomingGarmentStatBuilder()
.id(UUID.randomUUID())
.endDateInRange(toDate)
.range(DAILY)
.org(organization)
.value(sumValueByOrg(dailyOnceStats, organization))
.build())
.collect(Collectors.toList());
不學無術的我更難以理解這一堆方法調用是在干什么了秧廉。。拣帽。
不過沒有關系,看不懂這一堆方法調用减拭,可以一個一個來蔽豺。
可以從對我而言比較奇怪的這個Builder類的構造方法入手,點進去看看就好了拧粪。
這不看還好修陡,一看真是嚇一跳。這個Builder類長成了這個樣子:
public class IncomingGarmentStatBuilder {
private IncomingGarmentStat incomingGarmentStat;
public IncomingGarmentStatBuilder() {
incomingGarmentStat = new IncomingGarmentStat();
}
public IncomingGarmentStatBuilder(IncomingGarmentStat incomingGarmentStat) {
this.incomingGarmentStat = incomingGarmentStat;
}
public IncomingGarmentStat build() {
return incomingGarmentStat;
}
public IncomingGarmentStatBuilder id(UUID id) {
incomingGarmentStat.setId(id);
return this;
}
public IncomingGarmentStatBuilder range(StatisticRange range) {
incomingGarmentStat.setRange(range);
return this;
}
....
}
可以看出既们,這個Builder類是對所構造的類的set方法在此進行了一次封裝濒析,使得可以通過調用與屬性名相同的方法名為當前所構建的對象set屬性值。
那么問題來了啥纸,當我在創(chuàng)建一個實體類号杏,一般也就是一個Java Bean時,都會設計這個類的get、set方法盾致,并通過這些方法給所要創(chuàng)建的對象賦值主经。
簡單來說,例如對于一個People類而言庭惜,
public class People {
private int id;
private String name;
private int age;
private String gender;
public People() {
}
public People(int id, String name, int age, String gender) {
this.id = id;
this.name = name;
this.age = age;
this.gender = gender;
}
public setId(int id) {
this.id = id;
}
public getId() {
return id;
}
public setName(String name) {
this.name = name;
}
public getName() {
return name;
}
public setAge(int age) {
this.age = age;
}
public getAge() {
return age;
}
public setGender(String gender) {
this.gender = gender;
}
public getGender() {
return gender;
}
}
一般都長成上面這樣罩驻。按照這種大眾化的People類的設計方式,我去創(chuàng)建一個新的People對象時护赊,可以有兩種方法:
- 直接調用構造方法惠遏,把給定的參數(shù)通過構造方法傳入,如下:
People person = new People(9527, "華安", 24, "男")骏啰;
- 通過新建一個空對象使用set方法完成對象構建节吮,如下:
People person = new People();
person.setId(9527);
person.setName("華安");
person.setAge(24);
person.setGender("男");
這兩種方法也是我常用的方法。其實對于上述例子中的People判耕,因為屬性較少透绩,我們通過上述的兩種方式去創(chuàng)建實例對象都還較為方便易懂。但可以設想一下壁熄,若對于一個含有多種屬性的復雜類而言帚豪,使用這種構造器傳參或者使用set方法完成初始化實例對象的工作會帶來什么問題?
- 對于通過構造器傳參創(chuàng)建新對象草丧,在參數(shù)較多的情況下狸臣,會使得傳入?yún)?shù)和其對應的參數(shù)含義分離,對于讀代碼的人而言方仿,不會一下就能明白這些參數(shù)的意義何在固棚。
- 對于set方法傳參而言,顯而易見的是在大量屬性需要set的情況下仙蚜,需要調用多種的set方法此洲,從而使得在構建一個對象的過程中,使得代碼過于冗長委粉。
因此呜师,Builder類的采用,也就是建造者模式贾节,可以再保證代碼可讀性的前提下汁汗,使對象的創(chuàng)建變得靈活、簡潔栗涂。對于上述的People類知牌,采用建造者模式后,其PeopleBuilder類設計如下:
public class PeopleBuilder {
private People person;
public PeopleBuilder() {
person = new People();
}
public PeopleBuilder(People person) {
this.person = person;
}
public PeopleBuilder id(int id) {
person.setId(id);
return this;
}
public PeopleBuilder name(String name) {
person.setName(name);
return this;
}
public PersonBuilder age(int age) {
person.setAge(age);
return this;
}
public PersonBuilder gender(String gender) {
person.setGender(gender);
return this;
}
public People build() {
return person;
}
}
如此一來斤程,如果要創(chuàng)建一個People對象角寸,就可以用如下方式:
People person = new PeopleBuilder().id(9527)
.name("華安")
.age(24)
.gender("男")
.build();
如此一來相比之前的set方法就可以顯得十分簡潔明了。并且當這種方式嵌入到Stream的處理流中,或者Lambda表達式中時扁藕,也是毫無違和感的沮峡。
這就是建造者模式在Java中的使用了。