您的位置:首页 > 产品设计 > UI/UE

2. 【创建和销毁对象】用构建器创建复杂参数对象(Builder模式)

2015-12-31 22:50 555 查看
本文是《Effective Java》读书笔记的第二条。

废话不多说,先上个例子吧。我们一开始学程序开发,写完Hello world之后,通常就开始跟student较劲了。。。

学生通常有多个属性,比如学号、姓名、性别、年龄、年级、邮箱、家庭住址等等。

那么问题来了,在创建学生对象的时候若要初始化不同的参数,得弄多少个构造方法啊,这恐怕是个排列组合问题。如果用前一篇介绍到的静态工厂方法也比较麻烦。

通常大家会采用层叠构造器,如下(我真的不是为了凑字数。。。)

public class Student {

private String id;
private String name;
private String sex;
private int age;
private String grade;
private String email;
private String addr;

public Student() {
}

public Student(String id) {
this(id, null);
}

public Student(String id, String name) {
this(id, name, null);
}

public Student(String id, String name, String sex) {
this(id, name, sex, 0);
}

public Student(String id, String name, String sex, int age) {
this(id, name, sex, age, null);
}

public Student(String id, String name, String sex, int age, String grade) {
this(id, name, sex, age, grade, null);
}

public Student(String id, String name, String sex, int age, String grade,
String email) {
this(id, name, sex, age, grade, email, null);
}

public Student(String id, String name, String sex, int age, String grade,
String email, String addr) {
super();
this.id = id;
this.name = name;
this.sex = sex;
this.age = age;
this.grade = grade;
this.email = email;
this.addr = addr;
}
}


那么问题有来了,我只想初始化姓名和邮箱用哪个?还有这么多构造器都看乱套了,顺序一不小心就搞错,有木有其他办法?

还有一种办法就是采用JavaBeans模式,先用无参构造方法来创建对象,然后调用setter方法来依次设置每个需要设置的值。

嗯~问题解决了嘛,等等,好像有什么不妥~~~

由于几个参数的setter过程是分离的,因此在构造过程中有可能处于不一致的状态;

如果对象是不可变的话,就无法使用setter的方式了。

铺垫了这么多,还是赶紧清楚本文主角——Builder模式吧。

使用构建器Builder,你可以这样创建对象:

Student stu = new Student.Builder("20150102", "Tom").sex("male").age(12)
.grade("Grade 1").email("tom@163.com").addr("铁岭鸟不拉屎村").builder();


怎么样,是不是清晰了很多,而且规避了刚才JavaBeans方式的两个问题。那么如何实现刚才的效果呢?

public class Student {

private String id;
private String name;
private String sex;
private int age;
private String grade;
private String email;
private String addr;

public static class Builder {
// 必须初始化的属性
private String id;
private String name;
// 可选属性
private String sex;
private int age;
private String grade;
private String email;
private String addr;

public Builder(String id, String name) {
this.id = id;
this.name = name;
}

public Builder sex(String sex) { this.sex = sex; return this; }

public Builder age(int age) { this.age = age; return this; }

public Builder grade(String grade) { this.grade = grade; return this; }

public Builder email(String email) { this.email = email; return this; }

public Builder addr(String addr) { this.addr = addr; return this; }

public Student builder() {
return new Student(this);
}
}

public Student(Builder builder) {
this.id = builder.id;
this.name = builder.name;
this.sex = builder.sex;
this.age = builder.age;
this.grade = builder.grade;
this.email = builder.email;
this.addr = builder.addr;
}
}


不用我多说,从刚才的代码你首先感觉到的就是高度的灵活性,此外:

Builder可以对其参数添加约束条件,也就是在build()方法中进行检验;

Builder可以自动填充某些域,比如每次创建对象时自动增加序列号;

这种方式可以实现抽象工厂,比如下边的接口,刚才代码里的
Student.Builder
其实就是
Builder<Student>
的实现;相对于
Class.newInstance()
这种Java中传统的抽象工厂实现,有如下优点

newInstance()
方法总是调用类的无参构造方法,当然也许不存在无参构造方法,这时候并没有编译错误;

对于
newInstance()
方法客户端必须处理InstantiationException或者IllegalAccessException,而且
newInstance()
方法还会传递无参构造器抛出的异常,即使它本身缺乏相应的throws子句,因此它实际上破坏了编译时的异常检查。

public interface Builder<T> {
public T buile();
}


当然Builder模式也有不足:

为了创建对象必须先创建它的构建器,在某些非常注重性能的情况下,可能其开销就是问题了;

Builder模式相对来说,代码量更多,所以通常只有在很多参数的时候才使用,尤其是大多数参数都是可选的时候。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 读书笔记