遇到多个构造器参数时要考虑使用构建器
静态工厂和构造器都有个局限性,它们都不能很好地扩展到大量的可选参数.
问题
当一个类中的参数数量变得很多时,如何方便且灵活地创建含有不同参数的实例成了一个复杂的问题.
解决方案
1.普通方法
重叠构造器模式
(telescoping constructor),在这种模式下,提供的第一个构造器只包含必要的参数,第二个构造器包含一个可选参数,第三个构造器包含两个可选参数,以此类推.
public class Common {
static class People {
private final int id;//必选
private final String name;//必选
private final int age; //可选
private final int grade;//可选
private final String school;//可选
public People(int id, String name, int age, int grade, String school) {
this.id = id;
this.name = name;
this.age = age;
this.grade = grade;
this.school = school;
}
public People(int id, String name, int age, int grade) {
this(id, name, age, grade, null);
}
public People(int id, String name, int age) {
this(id, name, age, 0);
}
public People(int id, String name) {
this(id, name, 0);
}
}
public static void main(String[] args) {
People p = new People(12,"网三");
}
}
当你创建实例的时候,就利用参数列表最短的构造器,但是该列表包含了要设置的所有参数.
缺点:
- 很明显参数越来越多的情况下,你需要为其设置默认值,并且编写也会变得越来越繁琐
- 在创建新的实例的时候,你必须了解传入参数表示什么,如果参数变得越来越多, 就会失去控制,同时要保证参数顺序正确.
2. JavaBeans模式
在这种模式下,先声明一个无参构造器来创建对象,然后使用setter方法设置必选参数和可选参数.
public class JavaBeans {
static class People {
private int id = -1;//必选
private String name = null;//必选
private int age = -1; //可选
private int grade = -1;//可选
private String school= null;//可选
public People(){}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setGrade(int grade) {
this.grade = grade;
}
public void setSchool(String school) {
this.school = school;
}
}
public static void main(String[] args) {
People p = new People();
p.setName("张三");
p.setId(1);
//.....
}
}
缺点:
- 在构造的过程中,JavaBean可能处于
不一致的状态
,这里的不一致状态应该指的是如果只提供构造器,不提供setter方法,这样类实例化之后就不能修改了(这种对象可以通过验证构造器参数有效性,来保证一致性),这种可以保证线程安全性,如果提供set方法,就不能保证了. - JavaBean模式使得将类做成不可变的可能性不存在.因为要提供setter方法,因而参数不能用final修饰.
3. Builder模式
这种模式既能保证重叠构造器模式的安全性,也能保证像JavaBeans模式一样的可读性.
它不直接生成新的对象,而是先让客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个builder对象.
然后客户端在Build对象上再调用类似于setter的方法,来设置每个可选的参数.最后,客户端调用无参的build方法生成的通常是不可变的对象.
示例:
public class People {
private final int id;//必选
private final String name;//必选
private final int age; //可选
private final int grade;//可选
private final String school;//可选
public static class build {
private int id = 1;//必选
private String name = null;//必选
private int age = 1; //可选
private int grade = 1;//可选
private String school = null;//可选
public build(int id, String name) { this.id = id;this.name = name; }
public build age(int val) {
age = val;
return this;
}
public build grade(int val) {
grade = val;
return this;
}
public build school(String val) {
school = val;
return this;
}
public People builer(){
return new People(this);
}
}
public People(build build) {
id = build.id;
name = build.name;
school = build.school;
age = build.age;
grade = build.grade;
}
@Override
public String toString() {
return "People{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", grade=" + grade +
", school='" + school + '\'' +
'}';
}
public static void main(String[] args) {
People p = new build(1,"22").age(1).grade(1).builer();
System.out.println(p.toString());
}
}
优点:
- 它可以有多个可变参数,因为build是利用单独的方法设置每一个参数
- 可以利用单个build创建多个对象.builder参数可以在调用build方法来创建对象期间进行调整,也可以随着不同的对象而改变.
缺点:
在某些注重性能的情况下,构建器带来的开销会变得越来越明显.