顾乔芝士网

持续更新的前后端开发技术栈

带你读Effective系列:创建对象-Builder构建器

一、引言

在创建对象时,我们有可能遇到一个类中构造器可选参数太多,不好初始化。我们最先想到的解决方案是重叠构造器、JavaBean模式,其实还有一种方案:Builder构建器

二、类比

2.1 重叠构造器

重叠构造器其实就是,构造器之间的调用。先让我们看下java.util.Date类中是怎么使用重叠构造器来初始化时间的:

//年月日
public Date(int year, int month, int date) {
    this(year, month, date, 0, 0, 0);
}
//年月日时分
public Date(int year, int month, int date, int hrs, int min) {
    this(year, month, date, hrs, min, 0);
}
//年月日时分秒
public Date(int year, int month, int date, int hrs, int min, int sec) {
    int y = year + 1900;
    // month is 0-based. So we have to normalize month to support Long.MAX_VALUE.
    if (month >= 12) {
        y += month / 12;
        month %= 12;
    } else if (month < 0) {
        y += CalendarUtils.floorDivide(month, 12);
        month = CalendarUtils.mod(month, 12);
    }
    BaseCalendar cal = getCalendarSystem(y);
    cdate = (BaseCalendar.Date) cal.newCalendarDate(TimeZone.getDefaultRef());
    cdate.setNormalizedDate(y, month + 1, date).setTimeOfDay(hrs, min, sec, 0);
    getTimeImpl();
    cdate = null;
}

看起来也不是很麻烦而且在一定程度上缓解参数多的问题,但是如果我们再加入毫秒、微秒、纳秒呢?这时随着参数的增加,利用重叠构造器初始化对象就会变得很难维护,而且客户端也变得不易调用和阅读。

2.2 Java Bean模式

什么是JavaBean模式?其实这个东西,我们经常用到:先调用一个无参构造器来创建对象,然后再调用 setter 方法来设置每个必要的参数,以及每个相关的可选参数。

还是以java.util.Date类为例,如下图:

这种模式弥补了重叠构造器模式的不足,用到哪个参数就设置哪个参数。创建实例也很容易,也方便阅读。

但是,有个问题,如果我们要把这个类做成不可变类,显然是不可能了。为什么?构造器已经公有化了。另外,在构建过程中JavaBean有可能会一直处于不一致的状态。为什么?因为在用setter不停的赋值,所以我们还要保证其线程安全。

2.3 Builder构建器

Builder构建器,它既能保证像重叠构造器模式那样的安全性,也能保证像JavaBean模式那么好的可读性。这就是建造者模式的一种形式 ,它不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个 builder 对象。然后,客户端在 bulder 对象上调用类似setter的方法,来设置每个相关的可选参数。最后,客户端调用无参 build 方法来生成通常是不可变的对象。

举个创建例子:

 public class TT {
    public static void main(String[] args){
        //测试
        WorkParams workParams = new WorkParams.Builder("11005517", 2)
                .amount(BigDecimal.TEN)
                .rountId(110)
                .startBranchCode("11992212")
                .stationId(1)
                .build();
        System.out.println(workParams);


    }
}


/**
 * 工作流参数
 */
class WorkParams{
    //起始机构
    private String startBranchCode;
    //当前审批机构
    private String currentBranchCode;
    //审批路线
    private int rountId;
    //当前站点
    private int stationId;
    //当前审批角色
    private int roleId;
    //审批金额
    private BigDecimal amount;


    public static class Builder{
        //起始机构
        private String startBranchCode;
        //当前审批机构
        private String currentBranchCode;
        //审批路线
        private int rountId;
        //当前站点
        private int stationId;
        //当前审批角色
        private int roleId;
        //审批金额
        private BigDecimal amount;


        /**
         * 构建器
         * @param currentBranchCode
         * @param roleId
         */
        public Builder(String currentBranchCode, int roleId) {
            this.currentBranchCode = currentBranchCode;
            this.roleId = roleId;
        }


        /**
         * 初始化WorkParams
         * @return
         */
        public WorkParams build(){
            return new WorkParams(this);
        }


        public Builder startBranchCode(String startBranchCode) {
            this.startBranchCode = startBranchCode;
            return this;
        }


        public Builder stationId(int stationId) {
            this.stationId = stationId;
            return this;
        }


        public Builder rountId(int rountId) {
            this.rountId = rountId;
            return this;
        }


        public Builder amount(BigDecimal amount) {
            this.amount = amount;
            return this;
        }
    }


    /**
     * 构造器私有化
     * @param builder
     */
    private WorkParams(Builder builder) {
        this.startBranchCode = builder.startBranchCode;
        this.currentBranchCode = builder.currentBranchCode;
        this.rountId = builder.rountId;
        this.stationId = builder.stationId;
        this.roleId = builder.roleId;
        this.amount = builder.amount;
    }


    @Override
    public String toString() {
        return "WorkParams{" +
                "startBranchCode='" + startBranchCode + '\'' +
                ", currentBranchCode='" + currentBranchCode + '\'' +
                ", rountId=" + rountId +
                ", stationId=" + stationId +
                ", roleId=" + roleId +
                ", amount=" + amount +
                '}';
    }
}

三、总结

当类的构造器或者静态工厂方法创建对象时中具有多个参数时,Builder构建器模式是一个不错的选择, 特别是当大多数参数都是可选或者类型相同的时候,使用 Builder 模式的客户端代码将更易于阅读和编写,而且构建过程中也比较安全。相应的随着参数的增加Builder构建器模式的代码量也相应的增加了,而且在构建上也比其它两种相对复杂一下,但这并不妨碍它的闪光点。

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言