当一个类有很多个成员变量时,如果我们还是采用构造器传参的方式创建对象,我们就需要一次性传很多参数。
如果提供的构造方法参数中包含了我们不想设置的参数,但是我们不得不为它传递值。
如下:
//姓名、年龄、性别、身高、爱好。。。。(即使 身高 未知,我们也不得不在此处传递一个""占位)
//当参数越来越多,将无法维护,一不留神穿错参数,或者顺序颠倒无法快速定位错误位置。
new User("张三","18","男","","打游戏");
接下来介绍两种替代方案:
第一种(JavaBeans模式):使用无参构造方法创建对象,再使用set方法来设置每个必要参数。
User user = new User();
user.setName("张三");
user.setAge("18");
...
这种方式的好处在于:我们创建对象变得很方便,并且我们只需要关注我们需要设置参数,为其设置值。
但是这种方式存在一个弊端:创建对象的整个过程被拆分了,在构造过程中JavaBean可能存在不一致的状态。另一点就是JavaBeans模式使得把类做成不可变的可能性不复存在。
第二种 (建造者模式):既能保证安全性又能保证可读性。
不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器,得到一个builder对象,然后客户端在 builder 对象上调用类似set的方法,来设置每个相关的可选参数。 最后 客户端调用无参 build 方法来生成通常是不可变的对象。
通过代码帮助我们理解
package lesson2;
/**
* @Description: user
* @author: caoshenyang
* @date: 2020.12.22
*/
public class User {
private String username;
private String age;
private String gender;
private String hobby;
/**
* 构建器
*/
public static class Builder {
// 必须的参数
private String username;
private String age;
// 可选的参数 可以在这里设置默认值
private String gender = "保密";
private String hobby;
public Builder(String username, String age) {
this.username = username;
this.age = age;
}
public Builder gender(String gender) {
this.gender = gender;
return this;
}
public Builder hobby(String hobby) {
this.hobby = hobby;
return this;
}
public User build() {
return new User(this);
}
}
private User(Builder builder) {
username = builder.username;
age = builder.age;
gender = builder.gender;
hobby = builder.hobby;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", age='" + age + '\'' +
", gender='" + gender + '\'' +
", hobby='" + hobby + '\'' +
'}';
}
}
User的构造器私有的,所有的参数都放在Builder里,builder的设置方式返回的是Builder本身,这样便于把调用链接起来,得到一个流式的API。
客户端代码:
/**
* @Description: 客户端
* @author: caoshenyang
* @date: 2020.12.23
*/
public class Client {
public static void main(String[] args) {
User build = new User.Builder("张三", "12").gender("男").hobby("打游戏").build();
System.out.println(build);
}
}
运行结果:
User{username='张三', age='12', gender='男', hobby='打游戏'}
Builder模式模拟了具名的可选参数,这样客户端编写代码变得更容易,并且可读性更好。
Builder模式同时也试用于类层次结构
使用平行层次结构的builder时,各自嵌套在相应的类中。抽象类有抽象的builder,具体类有具体的builder。
优点:
- builder(构建器)可以有多个可变参数,因为builder是利用单独的方法来设置每一个参数。
- 可以将多次调用传入的参数集中到一个域中。
- 十分灵活,利用单个builder可以创建多个对象。
- 参数可以在调用期间调整
- 可以自动填充某些域,比如创建某个类时自动增加序号
缺点:
- 创建对象时必须先创建它的构建器,在极度追求性能的环境下会成为问题。
- 代码更加冗长,适合参数比较多的时候使用。(作者建议4个以上)