您的位置:首页 > Web前端

Effective Java之多个构造参数考虑用构建器(二)

2017-12-12 13:56 656 查看
静态工厂方法和构造器都有一个共同的特点–>无法扩展到大量的参数。

对于大量的参数类,我们有以下方案:

1.重叠构造器。这个源码中经常可以看到,例如HashMap:

public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
}


也就是说每个HashMap调用的其实都是含有全部参数的构造器public HashMap(int initialCapacity, float loadFactor)

这种重叠构造器的方法在参数较少的时候,是一种不错的选择,可以参数多了的话呢?

参数顺序容易混淆吧?每个参数的意义容易难看懂吧?冗余的代码写得太多了吧?

于是方案二出现了

2.javaBean模式

就是把这个类声明成javabean,只需要调用他的无参构造方法,set,set,set就好了。

这样就避免了很多重叠构造器的问题

但是问题是,set,set,set的连续使用让javabean的构造过程分开来了,让javabean处于不一致的状态(状态就是javabean的属性集合,相邻两个set方法过后的javabean属性不一样,也就是状态不一致),因此,有些不小心的程序员,在set,set,set方法之间使用了javabean,因此可能会导致错误。

有没有更好的方案呢?

3.builder设计模式
以下是一个例子
public class User {

private final String firstName;   // 必传参数
private final String lastName;    // 必传参数
private final int age;            // 可选参数
private final String phone;       // 可选参数
private final String address;     // 可选参数

private User(UserBuilder builder) {
this.firstName = builder.firstName;
this.lastName = builder.lastName;
this.age = builder.age;
this.phone = builder.phone;
this.address = builder.address;
}

public String getFirstName() {
return firstName;
}

public String getLastName() {
return lastName;
}

public int getAge() {
return age;
}

public String getPhone() {
return phone;
}

public String getAddress() {
return address;
}

public static class UserBuilder {
private final String firstName;
private final String lastName;
private int age;
private String phone;
private String address;

public UserBuilder(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}

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

public UserBuilder phone(String phone) {
this.phone = phone;
return this;
}

public UserBuilder address(String address) {
this.address = address;
return this;
}

public User build() {
return new User(this);
}
}
}


*User*类的构造方法是私有的。也就是说调用者不能直接创建User对象。

*User*类的属性都是不可变的。所有的属性都添加了final修饰符,因此User是不可变的,并且在构造方法中设置了值。并且,对外只提供getters方法。

Builder模式使用了链式调用。可读性更佳。

Builder的内部类构造方法中只接收必传的参数,并且该必传的参数适用了final修饰符,可选参数没有final修饰符,因此可以用此builder修改。

调用builder构建User:

new User.UserBuilder("王", "小二")
.age(20)
.phone("123456789")
.address("亚特兰蒂斯大陆")
.build();


Builder模式好处和优点:

使用Builder模式必然会导致写两遍相关属性的代码和SETTER方法,看起来有点吃力不讨好。

然而需要看到的是,客户端代码的可用性和可读性得到了大大提高。与此同时,构造函数的参数数量明显减少调用起来非常直观。

用Builder的构造函数而不是set方法传递客户需要的属性。这样做的好处在于,对象总是能被一次完整的实例化,这能避免对象不一致状态带来可能的错误。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息