Builder(构造器模式)的书写


原博文,点击这里
@Builder的使用

1.通过构造器构建

假设一个场景:我们用一个class来表示车,车有一些必需的属性,比如:车身,轮胎,发动机,方向盘等。也有一些可选属性,假设超过10个,比如:车上的一些装饰,安全气囊等等非常多的属性。
如果我们用构造器来构造对象,我们的做法是 提供第一个包含4个必需属性的构造器,接下来再按可选属性依次重载不同的构造器,这样是可行的,但是会有以下一些问题:

  • 一旦属性非常多,需要重载n多个构造器,而且各种构造器的组成都是在特定需求的情况下制定的,代码量多了不说,灵活性大大下降
  • 客户端调用构造器的时候,需要传的属性非常多,可能导致调用困难,我们需要去熟悉每个特定构造器所提供的属性是什么样的,而参数属性多的情况下,我们可能因为疏忽而传错顺序。
public class Car {
    /**
     * 必需属性
     */
    private String carBody;//车身
    private String tyre;//轮胎
    private String engine;//发动机
    private String aimingCircle;//方向盘
    /**
     * 可选属性
     */
    private String decoration;//车内装饰品

    /**
     * 必需属性构造器
     *
     * @param carBody
     * @param tyre
     * @param engine
     */
    public Car(String carBody, String tyre, String engine) {
        this.carBody = carBody;
        this.tyre = tyre;
        this.engine = engine;
    }

    /**
     * 假如我们需要再添加车内装饰品,即在原来构造器基础上再重载一个构造器
     *
     * @param carBody
     * @param tyre
     * @param engine
     * @param aimingCircle
     * @param decoration
     */
    public Car(String carBody, String tyre, String engine, String aimingCircle, String decoration) {
        this.carBody = carBody;
        this.tyre = tyre;
        this.engine = engine;
        this.aimingCircle = aimingCircle;
        this.decoration = decoration;
    }
}

2.JavaBeans模式构建

提供无参的构造函数,暴露一些公共的方法让用户自己去设置对象属性,这种方法较之第一种似乎增强了灵活度,用户可以根据自己的需要随意去设置属性。但是这种方法自身存在严重的缺点:
因为构造过程被分到了几个调用中,在构造中 JavaBean 可能处于不一致的状态。类无法仅仅通过判断构造器参数的有效性来保证一致性。还有一个严重的弊端是,JavaBeans 模式阻止了把类做成不可变的可能。,这就需要我们付出额外的操作来保证它的线程安全。

public class Car {
    /**
     * 必需属性
     */
    private String carBody;//车身
    private String tyre;//轮胎
    private String engine;//发动机
    private String aimingCircle;//方向盘
    /**
     * 可选属性
     */
    private String decoration;//车内装饰品

    public void setCarBody(String carBody) {
        this.carBody = carBody;
    }

    public void setTyre(String tyre) {
        this.tyre = tyre;
    }

    public void setEngine(String engine) {
        this.engine = engine;
    }

    public void setAimingCircle(String aimingCircle) {
        this.aimingCircle = aimingCircle;
    }

    public void setDecoration(String decoration) {
        this.decoration = decoration;
    }
}

3.Builder 模式

我们用户一般不会自己来完成 car 组装这些繁琐的过程,而是把它交给汽车制造商。由汽车制造商去完成汽车的组装过程,这里的 Builder 就是汽车制造商,我们的 car 的创建都交由他来完成,我们只管开车就是啦, 先来个代码实际体验一下~

public final class Car {
    /**
     * 必需属性
     */
    final String carBody;//车身
    final String tyre;//轮胎
    final String engine;//发动机
    final String aimingCircle;//方向盘
    final String safetyBelt;//安全带
    /**
     * 可选属性
     */
    final String decoration;//车内装饰品
    /**
     * car 的构造器 持有 Builder,将builder制造的组件赋值给 car 完成构建
     * @param builder
     */
    public Car(Builder builder) {
        this.carBody = builder.carBody;
        this.tyre = builder.tyre;
        this.engine = builder.engine;
        this.aimingCircle = builder.aimingCircle;
        this.decoration = builder.decoration;
        this.safetyBelt = builder.safetyBelt;
    }
    ...省略一些get方法
    public static final class Builder {
        String carBody;
        String tyre;
        String engine;
        String aimingCircle;
        String decoration;
        String safetyBelt;

        public Builder() {
            this.carBody = "宝马";
            this.tyre = "宝马";
            this.engine = "宝马";
            this.aimingCircle = "宝马";
            this.decoration = "宝马";
        }
         /**
         * 实际属性配置方法
         * @param carBody
         * @return
         */
        public Builder carBody(String carBody) {
            this.carBody = carBody;
            return this;
        }

        public Builder tyre(String tyre) {
            this.tyre = tyre;
            return this;
        }
        public Builder safetyBelt(String safetyBelt) {
          if (safetyBelt == null) throw new NullPointerException("没系安全带,你开个毛车啊");
            this.safetyBelt = safetyBelt;
            return this;
        }
        public Builder engine(String engine) {
            this.engine = engine;
            return this;
        }

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

        public Builder decoration(String decoration) {
            this.decoration = decoration;
            return this;
        }
        /**
         * 最后创造出实体car
         * @return
         */
        public Car build() {
            return new Car(this);
        }
    }
}

现在我们的类就写好了,我们调用的时候执行一下代码:

 Car car = new Car.Builder()
                .build();

打断点,debug运行看看效果:
在这里插入图片描述
可以看到,我们默认的 car 已经制造出来了,默认的零件都是 “宝马”,滴滴滴~来不及解释了,快上车。假如我们不使用默认值,需要自己定制的话,非常简单。只需要拿到 Builder 对象之后,依次调用指定方法,最后再调用 build 返回 car 即可。下面代码示例:

        //配置car的车身为 奔驰
        Car car = new Car.Builder()
                .carBody("奔驰")
                .build();

依旧 debug 看看 car 是否定制成功~
在这里插入图片描述
咦,神奇的定制 car 定制成功了,话不多说,继续开车~~

我们在 Builder 类中的一系列构建方法中还可以加入一些我们对配置属性的限制。例如我们给 car 添加一个安全带属性,在 Buidler 对应方法出添加以下代码

 public Builder safetyBelt(String safetyBelt) {
            if (safetyBelt == null) throw new NullPointerException("没系安全带,你开个毛车啊");
            this.safetyBelt = safetyBelt;
            return this;
        }

然后调用的时候:

     //配置car的车身为 奔驰
     Car car = new Car.Builder()
                      .carBody("奔驰")
                      .safetyBelt(null)
                      .build();

我们给配置安全带属性加了 null 判断,一但配置了null 属性,即会抛出异常。好了 car 构建好了,我们来开车看看~

依旧 debug 开车走起~
在这里插入图片描述
bom~~~不出意外,翻车了。。。
最后有客户说了,你制造出来的 car 体验不是很好,想把车再改造改造,可是车已经出厂了还能改造吗?那这应该怎么办呢?不要急,好说好说,我们只要能再拿到 Builder 对象就有办法。下面我们给 Builder 添加如下构造,再对比下 Car 的构造看看有啥奇特之处:`

       /**
         * 回厂重造
         * @param car
         */
        public Builder(Car car) {
            this.carBody = car.carBody;
            this.safetyBelt = car.safetyBelt;
            this.decoration = car.decoration;
            this.tyre = car.tyre;
            this.aimingCircle = car.aimingCircle;
            this.engine = car.engine;
        }
  /**
     * car 的构造器 持有 Builder,将 builder 制造的组件赋值给 car 完成构建
     *
     * @param builder
     */
    public Car(Builder builder) {
        this.carBody = builder.carBody;
        this.tyre = builder.tyre;
        this.engine = builder.engine;
        this.aimingCircle = builder.aimingCircle;
        this.decoration = builder.decoration;
        this.safetyBelt = builder.safetyBelt;
    }


咦,似乎有着对称的关系,没错。我们提供对应的构造。调用返回对应的对象,可以实现返回的效果。在 Car 中添加方法

 /**
     * 重新拿回builder 去改造car
     * @return
     */
    public Builder newBuilder() {
        return new Builder(this);
    }

现在来试试能不能返厂重建?把原来的宝马车重造成奔驰车,调用代码:

Car newCar = car.newBuilder()
                .carBody("奔驰")
                .safetyBelt("奔驰")
                .tyre("奔驰")
                .aimingCircle("奔驰")
                .decoration("奔驰")
                .engine("奔驰")
                .build();

行,车改造好了,我们继续 debug ,试试改造完满不满意
`在这里插入图片描述
哈哈,已经改造好了,客户相当满意~~

分析

下面分析一下具体是怎么构建的。

  • 新建静态内部类 Builder ,也就是汽车制造商,我们的 car 交给他来制造,car 需要的属性 全部复制进来
  • 定义 Builder 空构造,初始化 car 默认值。这里是为了初始化构造的时候,不要再去特别定义属性,直接使用默认值。定义 Builder 构造,传入 Car ,构造里面执行 Car 属性赋值 给 Builder 对应属性的操作,目的是为了重建一个builder 进行返厂重造
  • 定义一系列方法进行属性初始化,这些方法跟 JavaBeans 模式构建 中的方法类似,不同的是,返回值为 Builder 类型,为了方便链式调用。最后定义方法返回实体 Car 对象,car 的构造器 持有 Builder,最终将builder制造的组件赋值给 car 完成构建

至此,我们的 Builder 模式体验就结束了,这里讲的只是 Builder 模式的一个变种,即在 android 中应用较为广泛的模式,下面总结一下优缺点:
优点:

解耦,逻辑清晰。统一交由 Builder 类构造,Car 类不用关心内部实现细节,只注重结果。

链式调用,使用灵活,易于扩展。相对于方法一中的构造器方法,配置对象属性灵活度大大提高,支持链式调用使得逻辑清晰不少,而且我们需要扩展的时候,也只需要添加对应扩展属性即可,十分方便。

缺点:

硬要说缺点的话 就是前期需要编写更多的代码,每次构建需要先创建对应的 Builder 对象。但是这点开销几乎可以忽略吧,前期编写更多的代码是为了以后更好的扩展,这不是优秀程序员应该要考虑的事么

解决方法: 不会偷懒的程序猿不是好程序猿,针对以上缺点,IDEA 系列的 ide ,有相应的插件 InnerBuilder 可以自动生成 builder 相关代码,安装自行 google,使用的时候只需要在实体类中 alt + insert 键,会有个 build 按钮提供代码生成。
使用场景
一般如果类属性在4个以上的话,建议使用 此模式。还有如果类属性存在不确定性,可能以后还会新增属性时使用,便于扩展。

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值