我们在创建一个复杂的对象时,常常会遇到一个问题:对象的构造函数需要接收大量的参数,其中一些是必选的,而另一些是可选的。
比如你需要创建一个 Resume(简历)对象,其中包含姓名、年龄、教育背景等必选项,同时还可以有获奖情况、工作经验、公司名称等多个可选项。
如果通过构造函数来传递这些参数,可能会是这样:
class Resume {
private String name;
private int age;
private String education;
private String awards;
private String workExperience;
private String companyName;
public Resume(String name, int age, String education, String awards,
String workExperience, String companyName) {
this.name = name;
this.age = age;
this.education = education;
this.awards = awards;
this.workExperience = workExperience;
this.companyName = companyName;
}
}使用时就类似这样:
// Tom 没有工作经验、公司名称等信息
Resume resume1 = new Resume("Tom", 23, "Bachelor", "Some Award", null, null);
// Jack 有工作经验、公司名称等信息
Resume resume2 = new Resume("Jack", 30, "Master", null, "XX Engineer", "Company A");这种方式可以完成对象的创建,但是问题也很明显:
1、构造函数的参数列表很长。
2、当某些可选参数不存在时,我们不得不传入 null,这使得代码非常难以阅读和维护。
3、如果需要为对象新增参数,就要修改构造函数,导致所有使用到这个构造函数的代码都需要新增这个参数。
为了解决这些问题,我们可以使用本文要介绍的生成器模式(Builder Pattern)。
生成器模式是一种创建型设计模式,简单来说就是通过一个 Builder 类来一步一步地构建对象,最后再通过一个 build() 方法生成最终的对象。
我们来看下如何用生成器模式来改造 Resume 类的创建过程,首先创建一个 ResumeBuilder 类用于构建 Resume 对象:
class ResumeBuilder {
private String name;
private int age;
private String education;
private String awards;
private String workExperience;
private String companyName;
public ResumeBuilder setBasicInfo(String name, int age, String education) {
this.name = name;
this.age = age;
this.education = education;
return this;
}
public ResumeBuilder setAwards(String awards) {
this.awards = awards;
return this;
}
public ResumeBuilder setWorkExperience(String workExperience) {
this.workExperience = workExperience;
return this;
}
public ResumeBuilder setCompanyName(String companyName) {
this.companyName = companyName;
return this;
}
public Resume build() {
if (name == null || age == 0 || education == null) {
throw new IllegalArgumentException("Name, age and education are required");
}
return new Resume(name, age, education, awards, workExperience, companyName);
}
}ResumeBuilder 类的关键在于它的每个方法都返回自身,也就是返回 Builder 对象本身,这样调用完一个方法后可以继续在同一个对象上调用下一个方法,形成链式调用:
Resume resume1 = new ResumeBuilder()
.setBasicInfo("Tom", 23, "Bachelor")
.setAwards("Some Award")
.build();
Resume resume2 = new ResumeBuilder()
.setBasicInfo("Jack", 30, "Master")
.setWorkExperience("XX Engineer")
.setCompanyName("Company A")
.build();通过这种方式,代码的可读性大大提高,每个方法名清楚地表明了在设置哪个属性。可选属性按需设置就好,不需要传一堆 null。
点击运行看看效果:
上面这个简单的例子就展示了生成器模式,主要有以下几个关键角色:
- 产品(Product):表示被构建的复杂对象,即上面的
Resume类。 - 生成器(Builder):即上面的
ResumeBuilder类,它提供了链式调用方法来设置Resume类的各个字段,并提供一个build()方法来创建Resume对象。
上述例子是最简单常用的生成器模式,当然你也可以根据实际情况变通。
比如你可以把 Resume 类的数据字段和构造函数都设置为私有的,这样就能限制调用方只能通过 ResumeBuilder 来创建对象。
再比如,可以将 ResumeBuilder 作为 Resume 类的内部类并提供一个 builder() 方法创建生成器对象,这样可以让代码实现更紧凑简洁:
class Resume {
// ...
private static class ResumeBuilder {
// ...
}
public static ResumeBuilder builder() {
return new ResumeBuilder();
}
}
// 这样使用
Resume resume = Resume.builder()
.setBasicInfo("Tom", 23, "Bachelor")
.setAwards("Some Award")
.build();