杰瑞科技汇

Java Builder模式究竟是什么?又该如何在实际开发中使用?

这是一个非常常用且重要的设计模式,主要用于解决构造函数参数过多配置对象复杂的问题。

Java Builder模式究竟是什么?又该如何在实际开发中使用?-图1
(图片来源网络,侵删)

为什么需要 Builder 模式?(问题背景)

想象一下,你要创建一个 Computer 对象,它有很多可选的配置项:

public class Computer {
    private String cpu;      // 必需
    private String ram;      // 必需
    private int storage;     // 可选
    private String gpu;      // 可选
    private boolean hasMouse; // 可选
    private boolean hasKeyboard; // 可选
    // 问题来了:如何构造这个对象?
    // 方案一:使用重载构造函数
    public Computer(String cpu, String ram) { ... }
    public Computer(String cpu, String ram, int storage) { ... }
    public Computer(String cpu, String ram, int storage, String gpu) { ... }
    // ... 无穷无尽的组合,不现实,且代码冗余。
    // 方案二:使用“巨型”构造函数,所有参数都设为可选(用默认值)
    public Computer(String cpu, String ram, int storage, String gpu, boolean hasMouse, boolean hasKeyboard) {
        this.cpu = cpu;
        this.ram = ram;
        this.storage = storage;
        this.gpu = gpu;
        this.hasMouse = hasMouse;
        this.hasKeyboard = hasKeyboard;
    }
}

使用“巨型”构造函数的客户端代码会是这样:

// 糟糕的可读性!你不知道第三个参数是 storage 还是 gpu
Computer gamingPC = new Computer("Intel i9", "32GB", 1000, "NVIDIA RTX 4090", true, true);

这就是著名的 “构造器参数爆炸”“参数不可读” 问题,Builder 模式就是为了优雅地解决这些问题而生的。


Builder 模式的核心思想

Builder 模式的核心思想是:将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。

Java Builder模式究竟是什么?又该如何在实际开发中使用?-图2
(图片来源网络,侵删)

它包含以下几个角色:

  1. Product (产品类):最终要构建的复杂对象。Computer
  2. Builder (抽象构建器):定义创建产品各个部件的抽象接口。ComputerBuilder
  3. ConcreteBuilder (具体构建器):实现 Builder 接口,构建和装配产品的各个部件,并返回最终产品。GamingComputerBuilder
  4. Director (指挥者):使用 Builder 接口来构建产品,它不关心具体的构建细节,只负责按一定流程调用 Builder 的方法,在某些简化实现中,Director 可以被省略,由客户端直接使用 Builder

经典实现(带 Director)

我们先来看一个完整的、包含所有角色的经典实现。

1 1. Product (产品类) - Computer.java

这个类通常会将构造函数设为 private,以防止客户端直接通过 new 来创建实例,同时提供一个静态的 builder() 方法,返回一个 Builder 实例。

public class Computer {
    // 1. 所有字段设为 private final,保证对象不可变(Immutable),这是 Builder 模式的最佳实践
    private final String cpu;
    private final String ram;
    private final int storage;
    private final String gpu;
    private final boolean hasMouse;
    private final boolean hasKeyboard;
    // 2. 私有构造函数,只能通过 Builder 来创建
    private Computer(Builder builder) {
        this.cpu = builder.cpu;
        this.ram = builder.ram;
        this.storage = builder.storage;
        this.gpu = builder.gpu;
        this.hasMouse = builder.hasMouse;
        this.hasKeyboard = builder.hasKeyboard;
    }
    // 3. 提供一个静态的 builder() 方法,返回构建器实例
    public static Builder builder() {
        return new Builder();
    }
    // Getters...
    public String getCpu() { return cpu; }
    public String getRam() { return ram; }
    // ... 其他 getters
    // 4. 在 Product 类内部定义 Builder 静态内部类
    public static class Builder {
        // 5. Builder 的字段与 Product 的字段一一对应
        private String cpu;
        private String ram;
        private int storage = 0; // 可选参数提供默认值
        private String gpu;
        private boolean hasMouse = false;
        private boolean hasKeyboard = false;
        // 6. 私有构造函数,通过外部类的 builder() 方法来实例化
        private Builder() {}
        // 7. 为每个可选参数提供 setter 方法,并返回 this(链式调用)
        public Builder cpu(String cpu) {
            this.cpu = cpu;
            return this;
        }
        public Builder ram(String ram) {
            this.ram = ram;
            return this;
        }
        public Builder storage(int storage) {
            this.storage = storage;
            return this;
        }
        public Builder gpu(String gpu) {
            this.gpu = gpu;
            return this;
        }
        public Builder hasMouse(boolean hasMouse) {
            this.hasMouse = hasMouse;
            return this;
        }
        public Builder hasKeyboard(boolean hasKeyboard) {
            this.hasKeyboard = hasKeyboard;
            return this;
        }
        // 8. 提供 build() 方法,用于最终创建并返回 Product 实例
        public Computer build() {
            // 可以在这里进行参数校验
            if (cpu == null || ram == null) {
                throw new IllegalStateException("CPU and RAM are required!");
            }
            return new Computer(this);
        }
    }
    @Override
    public String toString() {
        return "Computer{" +
                "cpu='" + cpu + '\'' +
                ", ram='" + ram + '\'' +
                ", storage=" + storage +
                ", gpu='" + gpu + '\'' +
                ", hasMouse=" + hasMouse +
                ", hasKeyboard=" + hasKeyboard +
                '}';
    }
}

2 2. Director (指挥者) - ComputerDirector.java

Director 负责定义一个固定的构建流程。

public class ComputerDirector {
    public Computer constructGamingComputer(Computer.Builder builder) {
        return builder
                .cpu("Intel i9")
                .ram("32GB")
                .storage(1000)
                .gpu("NVIDIA RTX 4090")
                .hasMouse(true)
                .hasKeyboard(true)
                .build();
    }
    public Computer constructOfficeComputer(Computer.Builder builder) {
        return builder
                .cpu("Intel i5")
                .ram("16GB")
                .storage(512)
                .gpu("Integrated Graphics")
                .build(); // 其他可选参数使用默认值
    }
}

3 3. 客户端使用

public class Client {
    public static void main(String[] args) {
        // --- 方式一:直接使用 Builder(更常用,更灵活) ---
        System.out.println("--- 直接使用 Builder ---");
        Computer myPC = Computer.builder()
                .cpu("AMD Ryzen 7")
                .ram("16GB")
                .storage(512)
                .gpu("AMD Radeon RX 6700 XT")
                .build();
        System.out.println(myPC);
        // --- 方式二:使用 Director(适用于固定配置模板) ---
        System.out.println("\n--- 使用 Director ---");
        ComputerDirector director = new ComputerDirector();
        Computer gamingPC = director.constructGamingComputer(Computer.builder());
        System.out.println(gamingPC);
        Computer officePC = director.constructOfficeComputer(Computer.builder());
        System.out.println(officePC);
    }
}

输出:

--- 直接使用 Builder ---
Computer{cpu='AMD Ryzen 7', ram='16GB', storage=512, gpu='AMD Radeon RX 6700 XT', hasMouse=false, hasKeyboard=false}
--- 使用 Director ---
Computer{cpu='Intel i9', ram='32GB', storage=1000, gpu='NVIDIA RTX 4090', hasMouse=true, hasKeyboard=true}
Computer{cpu='Intel i5', ram='16GB', storage=512, gpu='Integrated Graphics', hasMouse=false, hasKeyboard=false}

简化实现(无 Director,最常用)

在现代开发中,Director 角色用得相对较少,因为它的功能很容易被客户端直接调用 Builder 的链式方法所取代,我们通常只关注 ProductBuilder

上面的 Computer 类的实现就是最常用的简化版,它的优点非常突出:

  1. 可读性高Computer.builder().cpu(...).ram(...) 这种方式清晰明了,就像在描述如何配置一台电脑。
  2. 参数灵活:可以自由选择哪些参数需要设置,哪些使用默认值。
  3. 对象不可变:由于所有字段都是 final,一旦 build() 完成对象就不可变,这是线程安全和健壮性的保证。
  4. 参数校验:可以在 build() 方法中集中检查参数的有效性。

Builder 模式的优缺点

优点

  • 解决了构造函数参数过多的问题
  • 提高了代码的可读性,避免了“参数顺序”和“参数含义”的混淆。
  • 支持参数的默认值,客户端无需为每个可选参数都传值。
  • 对象不可变性:可以轻松创建不可变对象,这是 Java 并发编程的最佳实践之一。
  • 灵活的构造过程:可以在 build() 方法中添加校验逻辑,或者在构建器中维护一个状态,确保构建顺序(必须先设置 A 才能设置 B)。

缺点

  • 代码量增加:需要为每个需要使用 Builder 模式的类都编写一个对应的 Builder 内部类,增加了代码的复杂度。
  • 创建对象的过程不直观:相比于直接 new 一个对象,Builder 模式需要多一步调用 builder()build() 的过程。

Java 标准库中的 Builder 模式

Java 自身也大量使用了 Builder 模式,最典型的例子就是 StringBuilderStringBuffer

// StringBuilder 就是一个经典的 Builder 模式应用
StringBuilder sb = new StringBuilder(); // Builder
sb.append("Hello"); // 设置部件
sb.append(" ");
sb.append("World"); // 设置部件
String result = sb.toString(); // build() 方法,返回最终产品

另一个例子是 java.lang.StringBuffer,它们都遵循 Builder 模式的核心思想:逐步构建一个复杂的字符串,最后一次性生成结果。

在现代 Java 中,java.util.stream.Builder<T> 接口也体现了这种思想。


Lombok 的 @Builder 注解

为了简化 Builder 模式的实现,强大的 Lombok 库提供了 @Builder 注解,它可以通过一个注解自动为我们生成上面手动编写的所有 Builder 相关代码。

使用 Lombok 后,Computer 类可以简化为:

import lombok.Builder;
import lombok.ToString;
@Builder // Lombok 会自动生成一个静态内部类 Builder,并 builder() 和 build() 方法
@ToString // Lombok 自动生成 toString()
public class Computer {
    private final String cpu;
    private final String ram;
    private final int storage;
    private final String gpu;
    private final boolean hasMouse;
    private final boolean hasKeyboard;
}

客户端代码完全不变,但开发者无需再手动编写繁琐的 Builder 类,这是目前在实际项目中非常流行的用法。

特性 描述
核心目的 解决构造函数参数过多和可读性差的问题。
关键角色 Product (产品), Builder (构建器), Director (指挥者,可选)。
核心优势 高可读性灵活性支持默认值易于创建不可变对象
经典实现 Product 类中定义一个静态 Builder 内部类,提供链式 setter 和一个 build() 方法。
现代实践 广泛使用,是构建复杂配置对象的推荐方式,Lombok 的 @Builder 注解极大地简化了其实现。

Builder 模式是 Java 开发者工具箱中一个不可或缺的工具,掌握它能让你的代码更加优雅和健壮。

分享:
扫描分享到社交APP
上一篇
下一篇