Java中的Builder模式
作为一只小白,在看到下面这样额的代码时,还真是一脸懵逼。。。
new IncomingGarmentStatBuilder()
.id(UUID.randomUUID())
.endDateInRange(toDate)
.range(DAILY)
.org(organization)
.value(sumValueByOrg(dailyOnceStats, organization))
.build()
虽然可以猜到是在创建新对象,但是这种方式还真是诡异呢
再加上当这段代码混在java 8 的stream处理过程中时,像下面这样,
List 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属性值。
那么问题来了,当我在创建一个实体类,一般也就是一个Java Bean时,都会设计这个类的get、set方法,并通过这些方法给所要创建的对象赋值。
简单来说,例如对于一个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类的设计方式,我去创建一个新的People对象时,可以有两种方法:
- 直接调用构造方法,把给定的参数通过构造方法传入,如下:
People person = new People(9527, "华安", 24, "男");
- 通过新建一个空对象使用set方法完成对象构建,如下:
People person = new People();
person.setId(9527);
person.setName("华安");
person.setAge(24);
person.setGender("男");
这两种方法也是我常用的方法。其实对于上述例子中的People,因为属性较少,我们通过上述的两种方式去创建实例对象都还较为方便易懂。但可以设想一下,若对于一个含有多种属性的复杂类而言,使用这种构造器传参或者使用set方法完成初始化实例对象的工作会带来什么问题?
- 对于通过构造器传参创建新对象,在参数较多的情况下,会使得传入参数和其对应的参数含义分离,对于读代码的人而言,不会一下就能明白这些参数的意义何在。
- 对于set方法传参而言,显而易见的是在大量属性需要set的情况下,需要调用多种的set方法,从而使得在构建一个对象的过程中,使得代码过于冗长。
因此,Builder类的采用,也就是建造者模式,可以再保证代码可读性的前提下,使对象的创建变得灵活、简洁。对于上述的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;
}
}
如此一来,如果要创建一个People对象,就可以用如下方式:
People person = new PeopleBuilder().id(9527)
.name("华安")
.age(24)
.gender("男")
.build();
如此一来相比之前的set方法就可以显得十分简洁明了。并且当这种方式嵌入到Stream的处理流中,或者Lambda表达式中时,也是毫无违和感的。
这就是建造者模式在Java中的使用了。