likes
comments
collection
share

构建卓越:深入解析Java中的Builder模式与灵活对象创建

作者站长头像
站长
· 阅读数 25

概述:

工作中,new 一个对象,如果想要充实这个对象,要么手动set,要么使用构造函数,如果不同的地方使用不同的构造函数,则需要很多构造函数进行搭配或者疯狂的set。当然,也有同学说可以使用lombok的Builder 进行构建但是需要依赖lombok并且该组件有一定bug,很多项目不敢使用。故此针对两者给出对应优缺点介绍:

Lombok @Builder:

优点: - 减少样板代码:自动生成构建器代码,减少了手动编写的需要。 - 易于使用:添加一个注解就可以使用。 - 链式调用:生成的代码支持链式调用,使代码更简洁。 - 一致性:生成的代码风格一致,减少了人为错误。

缺点: - 需要插件:IDE 需要安装 Lombok 插件才能正确识别 Lombok 生成的代码。 - 编译时依赖:Lombok 是一个编译时的依赖,它在编译时期修改字节码。 - 难以调试:由于代码是自动生成的,可能会使得调试过程变得复杂。 - 可定制性差:相比手动实现的构建器,Lombok 提供的定制性较差。

手动实现的 Builder:

优点: - 高度可定制:可以根据需要添加自定义逻辑和验证。 - 无需额外插件:不依赖 IDE 插件或特定的构建工具。 - 透明性:所有代码都是可见的,便于理解和调试。

缺点: - 更多的样板代码:需要手动编写构建器类,增加了代码量。 - 一致性依赖于开发者:代码风格和一致性依赖于开发者的实现。 - 维护成本:随着类的字段增加,需要手动更新构建器类。

在选择使用 Lombok 的 @Builder 或手动实现的 Builder 时,需要根据项目的需要和开发团队的偏好来决定。如果项目中大量使用构建器模式且希望减少样板代码,Lombok 可能是一个好选择。但如果需要更多的控制和定制,或者想避免额外的编译时依赖,手动实现的构建器可能更合适。

故此,针对以上,我这边实现一个手动Builder工具类:

Builder工具类:

import org.apache.logging.log4j.util.TriConsumer;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * @Author derek_smart
 * @Date 2024/6/3 16:01
 * @Description
 * <p> Builder
 */
public class Builder<T> {
    private final Supplier<T> instanceSupplier;
    private final List<Consumer<T>> setters;

    private Builder(Supplier<T> instanceSupplier) {
        this.instanceSupplier = instanceSupplier;
        this.setters = new ArrayList<>();
    }

    public static <E> Builder<E> of(Supplier<E> instanceSupplier) {
        return new Builder<>(instanceSupplier);
    }

    public <P> Builder<T> with(BiConsumer<T, P> consumer, P p) {
        this.setters.add(instance -> consumer.accept(instance, p));
        return this;
    }

    public <P1, P2> Builder<T> with(TriConsumer<T, P1, P2> consumer, P1 p1, P2 p2) {
        this.setters.add(instance -> consumer.accept(instance, p1, p2));
        return this;
    }

    public T build() {
        T t = this.instanceSupplier.get();
        this.setters.forEach(c -> c.accept(t));
        return t;
    }
}

构建卓越:深入解析Java中的Builder模式与灵活对象创建

类和方法介绍

  • 字段:

    • instanceSupplier: 一个 Supplier<T> 类型的字段,用于提供一个新的对象实例。
    • setters: 一个 Consumer<T> 类型的列表,存储一系列的设置操作(setter)。
  • 构造函数:

    • private Builder(Supplier<T> instanceSupplier): 私有构造函数,接收一个 Supplier<T> 实例,该实例用于在 build 方法中提供新对象的实例。
  • 静态工厂方法:

    • public static <E> Builder<E> of(Supplier<E> instanceSupplier): 一个泛型静态方法,用于创建 Builder 实例。它接收一个 Supplier<E> 参数,用于后续提供新对象的实例。
  • with 方法:

    • public <P> Builder<T> with(BiConsumer<T, P> consumer, P p): 接收一个 BiConsumer<T, P> 和一个参数 p,将设置操作(setter)添加到 setters 列表中。BiConsumer 是一个函数式接口,接收两个参数,用于设置对象的一个属性。
    • public <P1, P2> Builder<T> with(TriConsumer<T, P1, P2> consumer, P1 p1, P2 p2): 接收一个自定义的 TriConsumer<T, P1, P2> 接口和两个参数,将设置操作(setter)添加到 setters 列表中。TriConsumer 是一个假设存在的自定义函数式接口,它接收三个参数。
  • build 方法:

    • public T build(): 创建对象实例,并遍历 setters 列表,将每个设置操作(setter)应用到对象实例上,然后返回这个配置好的对象实例。

    使用场景

Builder<T> 类的使用场景通常涉及以下情况:

  • 构造复杂对象:当你需要构造一个属性较多的对象,且这些属性不都是必需设置的时候,使用 Builder 类可以使构造过程更加清晰和灵活。

  • 不可变对象:对于不可变对象,一旦创建就不能更改其状态,使用构建器模式可以一次性设置所有属性,然后创建对象。

  • 参数过多的构造函数:如果一个类有多个构造函数,或者构造函数的参数列表很长,使用 Builder 类可以简化构造过程,提高代码的可读性。

  • 链式调用Builder 类允许链式调用 with 方法来连续设置属性,这使得代码更加简洁。

使用示例:

public class Person {  
    private String name;  
    private int age;  
    private String address;  
    //... get set 方法省略
}  
Person person = Builder.of(Person::new)  
    .with(Person::setName, "Derek")  
    .with(Person::setAge, 25)  
    .with(Person::setAddress, "上海市徐汇区钦州路100号")  
    .build();  

总结:

Builder<T> 类是一个通用的构建器(Builder)类,用于灵活地构造和配置任意类型 T 的对象实例。它采用了一种流式接口(Fluent Interface)和链式调用的方法来简化对象的创建和设置过程。

Builder 类是一种实现构建器模式的工具,旨在提供一种灵活、清晰且可维护的方法来构造复杂对象。通过链式调用和分步设置对象属性,Builder 类能够简化对象的创建过程,特别适用于具有多个属性的对象。Lombok 的 @Builder 注解自动化了构建器模式的实现,减少了样板代码,提高了开发效率,但牺牲了一定的可定制性和透明度。相比之下,手动实现的 Builder 类提供了更高的定制性和控制力,允许开发者在构建对象时加入自定义逻辑,但需要编写更多的代码并手动维护。选择哪种实现方式取决于项目需求、团队偏好以及对于代码透明度和可维护性的考量。

转载自:https://juejin.cn/post/7376532114105712691
评论
请登录