问题场景
有如下一个Person类,包含姓名、年龄、住址、电话字段,创建Person类时,要对属性赋值,并且在对象创建后不允许在更改了。
public class Person{
// 姓名
private String name;
// 年龄
private Integer age;
// 家庭住址
private String address;
// 手机号码
private Integer phone;
// setter getter constructor 省略
// ....
}
解决方案
1. constructor 构造
- 使用全参构造器 ,实参传入会大量包含null,代码丑陋不优雅
- 创建只有name属性的对象
new Person("zhang",null,null,null);
- 创建有name和phone
new Person("zhang",null,null,10086);
- 创建只有name属性的对象
- 构造器重载
构造器重载能有效避免实参出现大量null,但可想而知,类的属性多了以后,会出现很多的构造器。
2.setter注入
Person person = new Person();
person.setName("张三");
person.setAddress("地址");
person.setAge(11);
这样有两个问题,
- 多次出现person.setXXX,代码丑陋。
- 不能保证属性一定是在创建时赋值。
3.setter升级版 链式setter
传统set方法返回void,可以稍作修改,直接返回Person
public Person setName(String name){
this.name = name;
return this;
}
public Person setAge(Integer age){
this.age = age;
return this;
}
public Person setAddress(String address){
this.address = address;
return this;
}
public Person setPhone(Integer phone){
this.phone = phone;
return this;
}
此时代码就简化为
Person person = new Person()
.setName("张三")
.setAddress("地址")
.setAge(11);
set链式调用解决了重复出现person.set的问题,但是不能保证对象属性只在初始化时赋值。
4.builder 模式
根据setter链式调用的思路,演进出另一套解决方案
- 创建Person全参构造器
- 新建一个Builder类
public static class Builder{
// 姓名
private String name;
// 年龄
private Integer age;
// 家庭住址
private String address;
// 手机号码
private Integer phone;
public Builder name(String name){
this.name = name;
return this;
}
public Builder age(Integer age){
this.age = age;
return this;
}
public Builder address(String address){
this.address = address;
return this;
}
public Builder phone(Integer phone){
this.phone = phone;
return this;
}
public Person toBuilder(){
return new Person(name,age,address,phone);
}
}
- 在Person类提供一个静态方法返回Builder实例
public static Builder builder(){ return new Builder(); }
- 使用
此时的使用代码如下
Person person = Person.builder()
.address("北京")
.age(11)
.name("lisi")
.toBuilder();
优点:
简化客户端调用时代码,保证属性在实例创建时赋值
缺点:
要编写Builder类,对provider来说代码量增加,且有大量冗余。
适用场景:
- 对象有很多属性需要赋值
- 在对象初始化后不再需要重新赋值
builder在开源框架中的应用:
- 在swagger配置ApiInfo时使用提供了builder模式创建,如下:
new ApiInfoBuilder()
// API 标题
.title("标题")
// API描述
.description("详情请百度")
// 版本号
.version(1.0)
.license("apache 2.0")
.build();
根据名称,很容易知道,目标类是ApiInfo,构造器是ApiInfoBuilder
查看源码,两者都包含如下共同字段,符合build模式的基础
private final String version;
private final String title;
private final String description;
private final String termsOfServiceUrl;
private final String license;
private final String licenseUrl;
private final Contact contact;
private List<VendorExtension> vendorExtensions
再查看build方法源码,就是使用全参构造器,创建一个目标实例。
public ApiInfo build() {
return new ApiInfo(title, description, version, termsOfServiceUrl, contact, license, licenseUrl, vendorExtensions);
}
网友评论