设计模式系列-构造器模式-Builder

案例

我们有个类,代表银行账户,有一些成员变量,代码如下:

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!

如果再次变更需求,需要增加更多的字段,此时这种创建对象的方法有几点缺点,

  1. 创建对象时,一行代码里有很多参数,可读性比较差,不知道参数对应的成员变量
  2. 当有连续相同类型的参数时,极易出现参数顺序错乱,而且此时java 编译器也无法发现
  3. 如果有些参数是可选的,只能通过 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 来解决这些问题。
当你发现一个类的字段不断增多,不断修改构造器时,或许此时应该考虑用构造器模式重构你的代码了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值