一、场景分析
在实际开发中,往往会遇到需要构建一个复杂的对象的代码,像这样的:
public class User {
private String name; // 必传
private String cardID; // 必传
private int age; // 可选
private String address; // 可选
}
于是我们起手就是撸了一串这样的代码,譬如:
通过构造函数的参数形式去写一个实现类
User(String name);
User(String name, String cardID);
User(String name, String cardID,int age);
User(String name, String cardID,int age, String address);
又或者通过设置setter和getter方法的形式写一个实现类
public class User {
private String name; // 必传
private String cardID; // 必传
private int age; // 可选
private String address; // 可选
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCardID() {
return cardID;
}
public void setCardID(String cardID) {
this.cardID = cardID;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
先说说这两种方式的优劣:
第一种在参数不多的情况下,是比较方便快捷的,一旦参数多了,代码可读性大大降低,并且难以维护,对调用者来说也造成一定困惑;
第二种可读性不错,也易于维护,但是这样子做对象会产生不一致的状态,当你想要传入全部参数的时候,你必需将所有的setXX方法调用完成之后才行。然而一部分的调用者看到了这个对象后,以为这个对象已经创建完毕,就直接使用了,其实User对象并没有创建完成,另外,这个User对象也是可变的,不可变类所有好处都不复存在。
写到这里真想为自己最近封装的表单控件捏一把汗。。。所以有没有更好地方式去实现它呢,那就是接下来要理解的Builder模式了。
二、定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的展示。
Builder模式属于创建型,一步一步将一个复杂对象创建出来,允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。
三、Builder模式变种-链式调用
代码实现
public class User {
private final String name; //必选
private final String cardID; //必选
private final int age; //可选
private final String address; //可选
private final String phone; //可选
private User(UserBuilder userBuilder){
this.name=userBuilder.name;
this.cardID=userBuilder.cardID;
this.age=userBuilder.age;
this.address=userBuilder.address;
this.phone=userBuilder.phone;
}
public String getName() {
return name;
}
public String getCardID() {
return cardID;
}
public int getAge() {
return age;
}
public String getAddress() {
return address;
}
public String getPhone() {
return phone;
}
public static class UserBuilder{
private final String name;
private final String cardID;
private int age;
private String address;
private String phone;
public UserBuilder(String name,String cardID){
this.name=name;
this.cardID=cardID;
}
public UserBuilder age(int age){
this.age=age;
return this;
}
public UserBuilder address(String address){
this.address=address;
return this;
}
public UserBuilder phone(String phone){
this.phone=phone;
return this;
}
public User build(){
return new User(this);
}
}
}
需要注意的点:
- User类的构造方法是私有的,调用者不能直接创建User对象。
- User类的属性都是不可变的。所有的属性都添加了final修饰符,并且在构造方法中设置了值。并且,对外只提供getters方法。
- Builder的内部类构造方法中只接收必传的参数,并且该必传的参数使用了final修饰符。
调用方式
new User.UserBuilder("Jack","10086")
.age(25)
.address("GuangZhou")
.phone("13800138000")
.build();
相比起前面通过构造函数和setter/getter方法两种方式,可读性更强。唯一可能存在的问题就是会产生多余的Builder对象,消耗内存。然而大多数情况下我们的Builder内部类使用的是静态修饰的(static),所以这个问题也没多大关系。
关于线程安全
Builder模式是非线程安全的,如果要在Builder内部类中检查一个参数的合法性,必需要在对象创建完成之后再检查,
正确示例:
public User build() {
User user = new user(this);
if (user.getAge() > 120) {
throw new IllegalStateException(“Age out of range”); // 线程安全
}
return user;
}