Java 8中實現構建器模式

banq發表於2024-04-23

在軟體開發過程中,我們經常遇到建立具有眾多屬性的物件變得令人生畏的場景。建構函式混亂會降低程式碼的可讀性。這正是構建器模式的閃光點。構建器模式是一種建立型設計模式,它將複雜物件的構造與其表示分離,提供了一種更清晰、更靈活的物件建立方法。

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(<font>"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;
    <font>// getters and setters<i>
}

現在,我們可以使用GenericBuilder建立一個GenericPost:

Post post = GenericBuilder.of(GenericPost::new)
  .with(GenericPost::setTitle, <font>"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(<font>"Java Builder Pattern")
  .text(
"Explaining how to implement the Builder Pattern in Java")
  .category(
"Programming")
  .build();

相關文章