Effective Java 第2条:遇到多个构造器参数时要考虑使用构建器
三种创建对象的方式。
目录
- 重叠构造器
- JavaBeans
- 构建器
Builder
#重叠构造器
/**
* Create By IntelliJ IDEA.
*
* @Author: Cheng
* @Date: 2017/10/30
* @Time: 19:21
*
* 层叠构造器
*/
public class User {
private String name; // required
private String password; // required
private int id; // optional
private String email; // optional
private String phone; // optional
private String address; // optional
public User(String name, String password, int id, String email, String phone, String address) {
this.name = name;
this.password = password;
this.id = id;
this.email = email;
this.phone = phone;
this.address = address;
}
public User(String name, String password) {
this(name, password, -1, null, null, null);
}
public User(String name, String password, int id) {
this(name, password, id, null, null, null);
}
public User(String name, String password, int id, String email) {
this(name, password, id, email, null, null);
}
public User(String name, String password, int id, String email, String phone) {
this(name, password, id, email, phone, null);
}
// 不提供 setter 方法,该对象就是不可变的了。
}
提供第一个只有必要参数的构造器,第二个构造器有一个可选参数,第三个有两个可选参数,依次类推,最后一个构造器包含所有可选参数。
重叠构造器模式可行,但是当有许多参数的时候,客户端代码会很难编写,并且依然较难阅读。
参数一多起来不阅读文档将很难理解创建对象该传什么参数 或者将参数顺序搞乱
#JavaBeans
/**
* Create By IntelliJ IDEA.
*
* @Author: Cheng
* @Date: 2017/10/30
* @Time: 19:21
*
* JavaBeans
*/
public class User {
private String name; // required
private String password; // required
private int id; // optional
private String email; // optional
private String phone; // optional
private String address; // optional
public User(String name, String password) {
this.name = name;
this.password = password;
}
// 省略getter and setter方法。
}
JavaBeans模式弥补了重叠构造器的不足。说的明白点,就是创建实例很容易,这样产生的代码读起来也很容易 get
set
但是JavaBeans模式有一个缺点。因为构造过程被分到了几个调用中,在构造过程中JavaBean可能处于不一致的状态,也就是说,JavaBean是非线程安全的。
JavaBeans模式阻止了把类做成不可变的可能。set
#构建器 Builder
/**
* Create By IntelliJ IDEA.
*
* @Author: Cheng
* @Date: 2017/10/30
* @Time: 18:34
*
* 构建器模式
*/
/**
* 用户类
*
* 帐号和密码必须输入
* id不赋值表示未存入数据库
* 邮箱、手机、地址为选填项
*/
public class User {
private String name; // required
private String password; // required
private int id; // optional
private String email; // optional
private String phone; // optional
private String address; // optional
/**
* 构造方法传入构造器
* @param builder
*/
private User(Builder builder) {
this.id = builder.id;
this.name = builder.name;
this.password = builder.password;
this.email = builder.email;
this.phone = builder.phone;
this.address = builder.address;
}
/**
* User类的构造器
* 选填属性设置默认值
*
* 必须传入的属性由构造器传入设置
* 选填属性由方法设置
*/
public static class Builder {
private String name;
private String password;
private int id = -1;
private String email = "未设置";
private String phone = "未设置";
private String address = "未设置";
public Builder(String name, String password) {
this.name = name;
this.password = password;
}
/**
* 返回的是建造器对象
* @param email
* @return
*/
public Builder setEmail(String email) {
this.email = email;
return this;
}
public Builder setPhone(String phone) {
this.phone = phone;
return this;
}
public Builder setAddress(String address) {
this.address = address;
return this;
}
public User build() {
return new User(this);
}
}
// 不提供 setter 方法,该对象就是不可变的了。也就是说,保证了同步性。
}
拥有重叠构造器模式的安全性,也能保证JavaBean模式那么好的代码可读性。这就是 Builder 模式的一种形式。
不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器,得到一个
builder
对象。然后客户端在 builder
对象上调用类似 setter
的方法来设置可选参数。最后调用无参的 build
方法来生成不可变的对象。
这个 builder 是它构建的类的静态成员类。
注意 User 被设计成不可变的了,所有的默认参数值都单独放在一个地方。builder 的 setter 方法返回 builder 本身,以便把调用链接起来。下面是客户端代码:
public static void main(String[] args) {
User user = new User.Builder("cheng", "********")
.setEmail("qq.com")
.setPhone("110")
.setAddress("HangZhou")
.build();
System.out.println(user);
}
builder 就像个构造器一样,可以对器参数强加约束条件。buld 方法可以检验这些约束条件。
public User build() {
// 校验逻辑
return new User(this);
}
不足
- 比起另外两种创建模式,Builder 模式多创建了一个构建器对象。
- 代码量比重叠构造器模式更加冗长。
#总结
如果类的构造器中具有多个参数,并且对安全有要求,设置这种类时,Builder 模式就是种不错的选择。