Java设计模式-建造者模式

一句话记住

"把复杂对象拆成 字段-填充-组装 三个阶段,先指挥 Builder 按序 一步步建造 ,最后 一次性交付 一个 完整且不可变 的对象。"

一、为什么要用建造者模式?

设想你要组装一台"高配台式机":

  • 机箱型号?CPU 型号?显卡?主板?电源?RGB?
  • 参数多、排列组合爆炸 → 构造器重载成几十种?
  • 必填与选填混杂,部分字段有顺序依赖 → 容易写出残次品对象。
  • 对象创建完应是不可变(Immutability),不允许二次 setXXX。

传统方案:

java 复制代码
// 错误示范:重叠构造器 Telescoping Constructor
Computer c1 = new Computer("NZXT", "AMD 7950X",   null, null, null,null, null);  // 错填
Computer c2 = new Computer("ABCD", "Intel 13900K","ASUS","RTX4090",null,null, "Seasonic 850W");

建造者方案:

java 复制代码
Computer pc = Computer.builder()
        .cpu("AMD Ryzen 9 7950X")
        .gpu("RTX 4090")
        .ram("32G DDR5")
        .build();

优点:

  • 可读性极高,接近自然语言 DSL。
  • 可选字段"不填也不会传 null"。
  • 复杂校验全部隐藏在 build() 里。
  • 生成的 Computer 无 setter,线程安全。

二、四种实现方式

版本 场景 JDK 支持 不可变 样板代码
传统 Builder 类 所有 JDK 自写
链式 setter 仅可变性要求低 自写 极少
Lombok @Builder 任何 >= 1.8 Lombok 插件 极少
Java 14+ record + Builder Java ≥ 14 语言支持

下面详细演示前两种,后两种给出关键代码片段。

三、传统 Builder 实现

1) 产品(Product)

java 复制代码
public final class Computer {

    // 必传
    private final String cpu;
    // 可选
    private final String gpu;
    private final int ram;
    private final Storage storage;
    private final boolean hasRGB;

    // 私有 ctor:只能是 Builder 调用
    private Computer(Builder b) {
        this.cpu   = b.cpu;
        this.gpu   = b.gpu;
        this.ram   = b.ram;
        this.storage = b.storage;
        this.hasRGB = b.hasRGB;
    }

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

    @Override
    public String toString() {
        return "Computer{cpu=" + cpu + ", gpu=" + gpu + ", ram=" + ram + "G}";
    }

    // ========= Builder 内部类 =========
    public static class Builder {
        // 必填字段在 Builder 中仍须初始 dummy,或 build 时检查
        private String cpu;   // 非 null
        private String gpu;
        private int ram = 8;  // 默认值
        private Storage storage = new Storage("SSD", 512);
        private boolean hasRGB;

        public Builder cpu(String cpu) {
            this.cpu = cpu;
            return this;         // 链式
        }
        public Builder gpu(String gpu) { this.gpu = gpu; return this; }
        public Builder ram(int ram)   { this.ram = ram; return this; }
        public Builder storage(String type, int size) {
            this.storage = new Storage(type, size);
            return this;
        }
        public Builder rgb(boolean flag) { this.hasRGB = flag; return this; }

        // 集中校验
        public Computer build() {
            Objects.requireNonNull(cpu, "CPU must not be null");
            if (ram < 4) throw new IllegalArgumentException("ram invalid");
            return new Computer(this);
        }
    }
}

2) 客户端代码

java 复制代码
Computer pc = Computer.builder()
        .cpu("Intel i9-14900K")
        .ram(64)
        .storage("PCIe4 NVMe", 2048)
        .gpu("RTX 4090")
        .rgb(true)
        .build();
System.out.println(pc);

四、Lombok 极简版本

只需一个注解:

java 复制代码
@Builder(toBuilder = true)
@Value  // = Immutable + getter + equals/hash/toString
public class Computer {
    String cpu;
    String gpu;
    int ram;
    Storage storage;
    boolean hasRGB;
}

Computer pc = Computer.builder().cpu("Ryzen").build();

@Value 生成的类 final,所有字段 final,无 setter,builder() 方法自动提供。

五、Java 14+ record + Builder

record 天生不可变,但缺少默认的 Builder。可手写一次,复用多次:

java 复制代码
public record Computer(String cpu, String gpu, int ram,
                       Storage storage, boolean hasRGB) {

    public static class Builder {
        // 省略同上
    }
}

六、与相近模式比较

  • 抽象工厂:返回 一组 相关对象(ProductA, ProductB),而 Builder 一步步构造一个复杂对象
  • 工厂方法:解决"哪种类来实例化",Builder 解决"如何用复杂步骤拼好这个实例"。
  • 原型模式:通过克隆 创建,Builder 是 从零建造
  • 装饰者:动态包装附加行为,Builder 静态地把字段锁定在构造期。

七、最佳实践小结

  1. 字段数 ≥ 4,且参数可选 → 优先考虑 Builder。
  2. 线程安全build() 之后对象不可变。
  3. Spring Boot @ConfigurationProperties 绑定支持 Builder:@ConstructorBinding + @Builder(Spring 2.2+)。
  4. 序列化 → 加 @Jacksonized(Lombok 1.18.14+)让 Jackson 也能用 Builder 反序列化 JSON。
  5. 分层架构Dozer / MapStruct 可自动从 DTO → Builder → Entity。

记住口诀:

"多参构造真痛苦,建造者模式来相助;链式调用表义明,一敲 build 对象成。"

相关推荐
瓯雅爱分享2 小时前
Java+Vue构建的采购招投标一体化管理系统,集成招标计划、投标审核、在线竞价、中标公示及合同跟踪功能,附完整源码,助力企业实现采购全流程自动化与规范化
java·mysql·vue·软件工程·源代码管理
mit6.8244 小时前
[C# starter-kit] 命令/查询职责分离CQRS | MediatR |
java·数据库·c#
诸神缄默不语4 小时前
Maven用户设置文件(settings.xml)配置指南
xml·java·maven
任子菲阳4 小时前
学Java第三十四天-----抽象类和抽象方法
java·开发语言
学Linux的语莫5 小时前
机器学习数据处理
java·算法·机器学习
找不到、了5 小时前
JVM的即时编译JIT的介绍
java·jvm
rongqing20195 小时前
Google 智能体设计模式:人机协同(HITL)
设计模式
西瓜er5 小时前
JAVA:Spring Boot 集成 FFmpeg 实现多媒体处理
java·spring boot·ffmpeg
你总是一副不开心的样子(´ . .̫ .5 小时前
一、十天速通Java面试(第三天)
java·面试·职场和发展·java面试
迎風吹頭髮6 小时前
UNIX下C语言编程与实践63-UNIX 并发 Socket 编程:非阻塞套接字与轮询模型
java·c语言·unix