在软件开发过程中,我们经常遇到创建具有众多属性的对象变得令人生畏的场景。构造函数混乱会降低代码的可读性。这正是构建器模式的闪光点。构建器模式是一种创建型设计模式,它将复杂对象的构造与其表示分离,提供了一种更清晰、更灵活的对象创建方法。
Builder模式的优点
在我们深入编码之前,让我们快速回顾一下利用构建器模式的优势:
- 灵活性——通过将构造过程与实际对象表示解耦,构建器模式允许我们创建具有不同配置的对象,而不会因多个构造函数或设置器而使我们的代码库混乱
- 可读性——Builder模式提供了流畅的接口,使我们的代码更具可读性;这使我们和其他开发人员能够一目了然地了解复杂对象的构造过程。
- 不变性——构建完成后,构建者可以通过创建不可变对象来强制不变性;这确保了线程安全并防止意外修改。
现在,让我们卷起袖子深入研究代码。经典构建器模式
在 Builder 模式的经典实现中,我们创建一个单独的Builder内部类。该内部类包含设置构造对象的每个属性的方法。这种结构化方法有利于顺序配置过程,确保清晰度和易用性。此外,它还增强了代码组织和可读性,使其更易于理解和维护:
public class Post { private final String title; private final String text; private final String category; Post(Builder builder) { this.title = builder.title; this.text = builder.text; this.category = builder.category; } public String getTitle() { return title; } public String getText() { return text; } public String getCategory() { return category; } public static class Builder { private String title; private String text; private String category; public Builder title(String title) { this.title = title; return this; } public Builder text(String text) { this.text = text; return this; } public Builder category(String category) { this.category = category; return this; } public Post build() { return new Post(this); } } }
|
在构建器类中,我们声明了外部类包含的同一组字段。Builder类提供了流畅的方法来设置 Post 的每个属性。此外,它还包含一个build()方法来创建Post实例。现在,我们可以使用Builder创建一个新对象:
Post post = new Post.Builder() .title("Java Builder Pattern") .text("Explaining how to implement the Builder Pattern in Java") .category("Programming") .build();
|
通用构建器模式
在 Java 8 中,lambda 表达式和方法引用开辟了新的可能性,包括更通用的构建器模式形式。我们的实现引入了一个GenericBuilder类,它可以利用泛型构造各种类型的对象:
public class GenericBuilder<T> { private final Supplier<T> supplier; private GenericBuilder(Supplier<T> supplier) { this.supplier = supplier; } public static <T> GenericBuilder<T> of(Supplier<T> supplier) { return new GenericBuilder<>(supplier); } public <P> GenericBuilder<T> with(BiConsumer<T, P> consumer, P value) { return new GenericBuilder<>(() -> { T object = supplier.get(); consumer.accept(object, value); return object; }); } public T build() { return supplier.get(); } }
|
此类遵循流畅的接口,从of()方法开始创建初始对象实例。然后,with()方法使用 lambda 表达式或方法引用设置对象属性。GenericBuilder提供了灵活性和可读性,使我们能够简洁地构造每个对象,同时确保类型安全。该模式展示了 Java 8 的表达能力,是复杂构建任务的优雅解决方案。
然而,一个很大的缺点是该解决方案基于类设置器。这意味着我们的属性不能再像前面的示例那样是最终的,从而失去了构建器模式提供的不变性。
对于下一个示例,我们将创建一个新的GenericPost类,其中包含默认的无参数构造函数、getter 和 setter:
public class GenericPost { private String title; private String text; private String category; // getters and setters }
|
现在,我们可以使用GenericBuilder创建一个GenericPost:Post post = GenericBuilder.of(GenericPost::new) .with(GenericPost::setTitle, "Java Builder Pattern") .with(GenericPost::setText, "Explaining how to implement the Builder Pattern in Java") .with(GenericPost::setCategory, "Programming") .build();
|
Lombok Builder
Lombok是一个库,它通过自动生成 getter、setter、equals、hashCode 甚至构造函数等常用方法来简化 Java 代码。
Lombok 最受赞赏的功能之一是它对构建器模式的支持。通过使用@Builder注释一个类,Lombok 生成一个具有用于设置属性的流畅方法的构建器类。此注释消除了手动构建器类实现的需要,显着减少了冗长
要使用 Lombok,我们需要从Maven 中央存储库导入依赖项:
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.32</version> </dependency>
|
现在,我们可以使用@Builder注释创建一个新的LombokPost类:@Builder @Getter public class LombokPost { private String title; private String text; private String category; }
|
我们还使用@Setter和@Getter注释来避免样板代码。然后我们可以使用开箱即用的构建器模式来创建新对象:LombokPost lombokPost = LombokPost.builder() .title("Java Builder Pattern") .text("Explaining how to implement the Builder Pattern in Java") .category("Programming") .build();
|