Java 中的建造者模式:清晰示例与实际应用

发布: (2025年12月31日 GMT+8 01:02)
8 min read
原文: Dev.to

Source: Dev.to

Java 中的建造者设计模式

The Builder design pattern is a 创建型 pattern that separates the construction of an object from its representation. In other words, it enables the creation of an object 一步一步, which is especially useful for complex objects.

何时应该使用 Builder 模式?

想象一个拥有 8 个属性的对象。使用 8 个参数的构造函数可以工作,但如果并不是所有属性都需要呢?为未使用的参数传入 null 是不好的做法,而且为每一种可能的组合都创建一个构造函数很快就会变得难以管理。

解决方案: 使用 Builder 模式,以提供一种可读、灵活的对象构建方式。

使用 Builder 模式的优势

优势描述
灵活性以自然、流畅的方式创建包含任意子集参数的对象。
可读性构造代码易于阅读和理解。
不可变性对象构建完成后可以是不可变的,确保线程安全,防止不必要的修改。

Builder 模式在 Java 中如何实现?

设计模式是抽象的解决方案,因此实现可以根据具体上下文进行调整。下面给出几个具体示例。

1. 经典 Builder 设计模式(Java)

经典做法是定义一个静态内部 Builder 类,它的字段与产品类的字段相对应。产品的字段为 private final(不可变),仅提供 getter。

package com.funcionaenmimaquina.builder.classic;

public class Product {
    private final String name;
    private final String description;
    private final double price;
    private final String category;
    private final String imageUrl;
    private final String brand;
    private final String sku;

    private Product(Builder builder) {
        this.name = builder.name;
        this.description = builder.description;
        this.price = builder.price;
        this.category = builder.category;
        this.imageUrl = builder.imageUrl;
        this.brand = builder.brand;
        this.sku = builder.sku;
    }

    /** Builder ----------------------------------------------------------- */
    public static class Builder {
        private String name;
        private String description;
        private double price;
        private String category;
        private String imageUrl;
        private String brand;
        private String sku;

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

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

        public Builder price(double price) {
            this.price = price;
            return this;
        }

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

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

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

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

        public Product build() {
            return new Product(this);
        }
    }

    /** Getters ----------------------------------------------------------- */
    public String getName()        { return name; }
    public String getDescription() { return description; }
    public double getPrice()       { return price; }
    public String getCategory()    { return category; }
    public String getImageUrl()    { return imageUrl; }
    public String getBrand()       { return brand; }
    public String getSku()         { return sku; }

    @Override
    public String toString() {
        return "Product{" +
                "name='" + name + '\'' +
                ", description='" + description + '\'' +
                ", price=" + price +
                ", category='" + category + '\'' +
                ", imageUrl='" + imageUrl + '\'' +
                ", brand='" + brand + '\'' +
                ", sku='" + sku + '\'' +
                '}';
    }
}

使用经典 Builder

static void runClassicBuilder() {
    System.out.println("Classic Builder Pattern Example");
    Product product = new Product.Builder()
            .name("Laptop")
            .description("High performance laptop")
            .price(1200.00)
            .category("Electronics")
            .imageUrl("http://example.com/laptop.jpg")
            .brand("BrandX")
            .sku("SKU12345")
            .build();

    System.out.println(product);
}

2. 使用 Lambda 的通用 Builder 设计模式

Lambda 让我们能够创建一个 通用 Builder,能够复用于任意类。

import java.util.function.BiConsumer;
import java.util.function.Supplier;

public class Builder<T> {
    private final Supplier<T> supplier;
    private final T instance;

    priv

> **Source:** ...

```java
public class Builder<T> {
    private final Supplier<T> supplier;
    private final T instance;

    private Builder(Supplier<T> supplier) {
        this.supplier = supplier;
        this.instance = supplier.get();
    }

    /** Factory method */
    public static <T> Builder<T> of(Supplier<T> supplier) {
        return new Builder<>(supplier);
    }

    /** Apply a setter (or any mutator) to the instance */
    public <P> Builder<T> with(BiConsumer<T, P> setter, P value) {
        setter.accept(instance, value);
        return this;
    }

    /** Return the fully built object */
    public T build() {
        return instance;
    }
}

示例:使用一个简单的 POJO

public class Person {
    private String firstName;
    private String lastName;
    private int age;

    // Setters (or any mutators) – they can be package‑private if you want
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public void setLastName(String lastName)   { this.lastName = lastName; }
    public void setAge(int age)               { this.age = age; }

    @Override
    public String toString() {
        return firstName + " " + lastName + ", age " + age;
    }
}
static void runGenericBuilder() {
    Person person = Builder.of(Person::new)
            .with(Person::setFirstName, "John")
            .with(Person::setLastName,  "Doe")
            .with(Person::setAge,       30)
            .build();

    System.out.println(person);
}

示例:使用 User

public class User {
    private String name;
    private String lastname;
    private String secondLastName;
    private String phone;
    private String email;

    // Getters and Setters needed
    public void setName(String name) { this.name = name; }
    public void setLastname(String lastname) { this.lastname = lastname; }
    public void setSecondLastName(String secondLastName) { this.secondLastName = secondLastName; }
    public void setPhone(String phone) { this.phone = phone; }
    public void setEmail(String email) { this.email = email; }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", lastname='" + lastname + '\'' +
                ", secondLastName='" + secondLastName + '\'' +
                ", phone='" + phone + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}
static void runGenericBuilder() {
    System.out.println("Generic Builder Pattern Example");
    User user = Builder.of(User::new)
            .with(User::setName, "John")
            .with(User::setLastname, "Doeh")
            .with(User::setSecondLastName, "Doeh")
            .with(User::setPhone, "555555")
            .with(User::setEmail, "mail@mail.com")
            .build();
    System.out.println(user.toString());
}

3. 使用 Lombok 实现 Java 中的 Builder 设计模式

Lombok 是一个通过注解消除样板代码的 Java 库。它提供了一种简洁的方式来实现 Builder 模式。

import lombok.Builder;
import lombok.Getter;
import lombok.ToString;

@Builder
@Getter
@ToString
public class Videogame {
    private String name;
    private String platform;
    private String category;
}

使用方式

static void runLombokBuilder() {
    System.out.println("Lombok Builder Pattern Example");
    Videogame videogame = Videogame.builder()
            .name("The Legend of Zelda")
            .platform("Nintendo Switch")
            .category("Action-Adventure")
            .build();
    System.out.println(videogame.toString());
}

Lombok 还支持在 record 上使用 @Builder 注解:

@Builder
public record SoccerTeam(String name, String country, String coach) { }

4. 使用 Java Records 实现 Builder 设计模式

Records 在 Java 14(预览)中引入,并在 Java 16 中正式标准化,能够在没有样板代码的情况下创建不可变类。如果你想为一个 record 不使用 Lombok 实现 Builder,可以自行编写实现。

手动实现:

public record Smartphone(String model, String brand, String operatingSystem, double price) {

    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {
        private String model;
        private String brand;
        private String operatingSystem;
        private double price;

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

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

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

        public Builder price(double price) {
            this.price = price;
            return this;
        }

        public Smartphone build() {
            return new Smartphone(model, brand, operatingSystem, price);
        }
    }
}

用法

static void runRecordBuilder() {
    System.out.println("Record Builder Pattern Example");
    Smartphone smartphone = Smartphone.builder()
            .model("iPhone 14")
            .brand("Apple")
            .operatingSystem("iOS")
            .price(999.99)
            .build();
    System.out.println(smartphone.toString());
}

结论

现在我们已经了解了 Builder 设计模式的用途,并且看到了在 Java 中实现它的几种方式。选择合适的实现方式取决于项目的具体情境和需求(例如,是否需要不可变性、减少样板代码、使用外部库等)。

相关链接

Back to Blog

相关文章

阅读更多 »

递归的迭代

将简化 HTML 转换为 Markdown 并使用语法树 在我的一个副项目或“宠物”项目中,我编写了一个小型 parser,用于简化 HTML,p...