2. 【创建和销毁对象】用构建器创建复杂参数对象(Builder模式)
2015-12-31 22:50
555 查看
本文是《Effective Java》读书笔记的第二条。
废话不多说,先上个例子吧。我们一开始学程序开发,写完Hello world之后,通常就开始跟student较劲了。。。
学生通常有多个属性,比如学号、姓名、性别、年龄、年级、邮箱、家庭住址等等。
那么问题来了,在创建学生对象的时候若要初始化不同的参数,得弄多少个构造方法啊,这恐怕是个排列组合问题。如果用前一篇介绍到的静态工厂方法也比较麻烦。
通常大家会采用层叠构造器,如下(我真的不是为了凑字数。。。)
那么问题有来了,我只想初始化姓名和邮箱用哪个?还有这么多构造器都看乱套了,顺序一不小心就搞错,有木有其他办法?
还有一种办法就是采用JavaBeans模式,先用无参构造方法来创建对象,然后调用setter方法来依次设置每个需要设置的值。
嗯~问题解决了嘛,等等,好像有什么不妥~~~
由于几个参数的setter过程是分离的,因此在构造过程中有可能处于不一致的状态;
如果对象是不可变的话,就无法使用setter的方式了。
铺垫了这么多,还是赶紧清楚本文主角——Builder模式吧。
使用构建器Builder,你可以这样创建对象:
怎么样,是不是清晰了很多,而且规避了刚才JavaBeans方式的两个问题。那么如何实现刚才的效果呢?
不用我多说,从刚才的代码你首先感觉到的就是高度的灵活性,此外:
Builder可以对其参数添加约束条件,也就是在build()方法中进行检验;
Builder可以自动填充某些域,比如每次创建对象时自动增加序列号;
这种方式可以实现抽象工厂,比如下边的接口,刚才代码里的
对于
当然Builder模式也有不足:
为了创建对象必须先创建它的构建器,在某些非常注重性能的情况下,可能其开销就是问题了;
Builder模式相对来说,代码量更多,所以通常只有在很多参数的时候才使用,尤其是大多数参数都是可选的时候。
废话不多说,先上个例子吧。我们一开始学程序开发,写完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对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树