案例
我们有个类,代表银行账户,有一些成员变量,代码如下:
public class BankAccount {
private long accountNumber;
private String owner;
private double balance;
public BankAccount(long accountNumber, String owner, double balance) {
this.accountNumber = accountNumber;
this.owner = owner;
this.balance = balance;
}
//Getters and setters omitted for brevity.
}
如果我们要初始化这个类,通过构造器new 一个对象
BankAccount account = new BankAccount(123L, "Zhangsan", 100.00);
这种创建对象的方式,目前来看简单明了,试想,如果业务变化,增加了许些字段,这时你该怎么应对需求呢?
比如,增加了两个字段:balance
,interestRate
, 一般代码重构如下,
public class BankAccount {
private long accountNumber;
private String owner;
private String branch;
private double balance;
private double interestRate;
public BankAccount(long accountNumber, String owner, String branch, double balance, double interestRate) {
this.accountNumber = accountNumber;
this.owner = owner;
this.branch = branch;
this.balance = balance;
this.interestRate = interestRate;
}
//Getters and setters omitted for brevity.
}
此时创建对象变为
BankAccount account = new BankAccount(456L, "Marge", "Springfield", 100.00, 2.5);
BankAccount anotherAccount = new BankAccount(789L, "Homer", null, 2.5, 100.00); //Oops!
如果再次变更需求,需要增加更多的字段,此时这种创建对象的方法有几点缺点,
- 创建对象时,一行代码里有很多参数,可读性比较差,不知道参数对应的成员变量
- 当有连续相同类型的参数时,极易出现参数顺序错乱,而且此时java 编译器也无法发现
- 如果有些参数是可选的,只能通过 Overload 多个构造器解决,想想多少组合,可怕。
那通过什么的方式来解决这些缺点呢,你可能会想到通过 setter 方法解决,此方法也有个缺点,在构建不可变对象时,会实例化一个部分属性初始化的对象,也就是出现中间状态对象。
有没有办法解决以上提到的问题,是构造器模式的出场的时候了。
Builder Pattern
当实例化复杂的对象时,构造器模式可以让我们写出已读、易懂的代码,而且提供了一套流式接口。
编写一个Builder 包含类 BankAccount 的所有字段,删除 类BankAccount的 public 构造器,提供一个 private构造器,只允许通过Builder 构造对象。
public class BankAccount {
public static class Builder {
private long accountNumber; //This is important, so we'll pass it to the constructor.
private String owner;
private String branch;
private double balance;
private double interestRate;
public Builder(long accountNumber) {
this.accountNumber = accountNumber;
}
public Builder withOwner(String owner){
this.owner = owner;
return this; //By returning the builder each time, we can create a fluent interface.
}
public Builder atBranch(String branch){
this.branch = branch;
return this;
}
public Builder openingBalance(double balance){
this.balance = balance;
return this;
}
public Builder atRate(double interestRate){
this.interestRate = interestRate;
return this;
}
public BankAccount build(){
BankAccount account = new BankAccount();
account.accountNumber = this.accountNumber;
account.owner = this.owner;
account.branch = this.branch;
account.balance = this.balance;
account.interestRate = this.interestRate;
return account;
}
}
private BankAccount() {
//Constructor is now private.
}
//Getters and setters omitted for brevity.
}
此时通过下面方式创建对象
BankAccount account = new BankAccount.Builder(1234L)
.withOwner("ZhanSan")
.atBranch("GongShan")
.openingBalance(100)
.atRate(2.5)
.build();
BankAccount anotherAccount = new BankAccount.Builder(4567L)
.withOwner("LiSi")
.atBranch("ZhaoShang")
.openingBalance(100)
.atRate(2.5)
.build();
此方式有点冗长,但是清晰已读,毕竟我们读代码时间比写代码的时间多得多。
总结
我们先通过简单的一个类,用 new 的方式创建对象简单明了,但是当需求变化,字段增多时,此时会暴露出很多缺点,这是我们引出 Builder 来解决这些问题。
当你发现一个类的字段不断增多,不断修改构造器时,或许此时应该考虑用构造器模式重构你的代码了。