该知识点是自己从书籍中学习的笔记。
背景:
当类的构造方法有很多参数(超过3个)或者参数类型基本是一样时,这时使用静态工程方法或者构造方法来创建对象,并不合适,因为客户在使用该类创建对象时,必须清楚每个参数代表的意义,稍不注意就会将参数值写错或者参数不够。
以下例子都是以Student类为基础,该类的必须成员属性:id(学生编号),name(姓名);可选成员属性:address,telephone,remark。
定义:
不直接创建带必须参数的对象,而是通过调用该类的一个内部静态成员Builder类(该类的构造方法含有所有必须参数(或者静态工程方法))得到一个builder对象。用户使用这个builder对象来设置对象各个可选的参数值,最后用户调用该builder对象的一个无参数的方法来获取的一个不变的Student对象。
关于背景中的问题处理方法:
1.采用“telescoping constructor pattern”(伸缩构造函数模式),即在编写多个构造方法,这些构造方法都必须填写上必要的参数,然后每个构造方法再添加不同的可选参数。例如:
// Telescoping constructor pattern - does not scale well!
public class Student {
private final String id;
private final String name;
private final int age;
private final String address;
public Student(String id, String name) {
this(id, name, 0, null);
}
public Student(String id, String name, int age) {
this(id, name, age, null);
}
public Student(String id, String name, String address) {
this(id, name, 0, address);
}
public Student(String id, String name, int age, String address) {
this.id = id;
this.name = name;
this.address = address;
this.age = age;
}
}
调用的时候,就如下:Student student = new Student (“12”, “san”, 10, “add”);
明显这种方式不太妥当,当有多个可选属性或者可选属性组合的时候,就特别不适合。
2.采用JavaBean的设计模式,先建立对象,然后再通过setter/getter的方式来设置值。如下:
public class Student1 {
private String id;
private String name;
private int age;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
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;
}
private String address;
}
当你调用的时候,如下:
Student1 s = new Student1();
s.setId(“1”);
……
使用用这种方式虽然可以解决方法一的问题,但是会导致创建的对象前后状态或数据信息不一致,因此你必须加强对象的安全管理,因为可以在该对象后面再设置状态或者数据信息。虽然可以使用当参数设置完全,并且在使用之前时,可以将该对象给冻结的方式来解决问题,但是这种方式在实际不适用。
3. 综合了上述两种方式各自的优点,称作“Builder pattern”来解决以上的两种方式的不足。实现过程:不直接创建带必须参数的Student对象,而是用户调用一个含有所有必须参数的构造方法(或者静态工程方法),得到一个builder对象。用户使用这个builder对象来设置Student各个可选的参数值,最后用户调用builer的一个无参数的方法来获取的一个不变的Student对象。在Student类中,builders是一个static member class。如下:
public class Student3 {
private final String id;
private final String name;
private final int age;
private final String address;
public static class Builder {
private final String id;
private final String name;
private int age;
private String address;
public Builder(String id, String name) {
this.id = id;
this.name = name;
}
public Builder age(int age) {
this.age = age;
return this;
}
public Builder address(String address) {
this.address = address;
return this;
}
public Student3 build() {
return new Student3(this);
}
}
public Student3(Builder builder) {
this.id = builder.id;
this.name = builder.name;
this.age = builder.age;
this.address = builder.address;
}
public static void main(String[] args) {
Student3 s = new Student3.Builder("1", "1").age(12).address("aaa").build();
}
}
优点:
1. 弥补了方法一、二的缺点;
2. Builder可以检查到属性的必须性和可选性;
3. Builder创建的可选性的属性都是一个方法,不重复;
4. 从jdk1.5开始可以使用泛型的方式来创建Builer,如:
// A builder for objects of type T
public interface Builder<T> {
public T build();
}
缺点:
1. 创建对象之前,必须要创建该对象的builder,这个对性能有些影响。
2. 在可选参数较多的时候,可以使用Builder模式。
适用场景:
当构造参数多,尤其是可选参数多时,可以优先考虑使用Builder模式。