大家好我是IT空门 · 门主。分享 AI 与代码顺便抢救发际线。一、为什么写这篇最近在改一个老项目赫然看到一段祖传代码newUser(张三,28,zhangsanxx.com,13800000000,北京市朝阳区,true,false,1,VIP,2024-01-01,...);我数了一下11 个参数。那一瞬间我血压上来了。这要是让你维护你敢动你敢改你敢加参数相信我每一个看这段代码的人脑子里都飘过同一句话“这是哪个天才写的”别笑90% 的 Java 项目里都埋着这种雷。而解决这个问题的银弹就是今天的主角——建造者模式Builder Pattern。二、什么是建造者模式1. 一句话定义建造者模式是一种创建型设计模式它将一个复杂对象的构建过程与它的表示分离使同样的构建过程可以创建出不同的表示。说人话就是“你要造一辆车不用关心发动机怎么装、轮胎怎么拧你只需要告诉 Builder我要 V8 发动机、19 寸轮毂、红色车漆。”Builder 帮你按顺序拼好你拿到就是一辆完整的车。2. 四个核心角色角色作用通俗理解Product产品最终要创建的复杂对象那辆组装好的车Builder抽象建造者定义构建步骤的接口装配手册ConcreteBuilder具体建造者实现具体的构建逻辑流水线工人Director指挥者控制构建顺序可选车间主任 实际开发中90% 的场景会省略 Director直接用链式调用 build()最经典的就是 Lombok 的Builder。三、Java 中哪些地方在大量用建造者模式如果你以为这只是设计模式书里的玩具那就太天真了。你每天写的代码背后都是它。1. JDK 自家兄弟// StringBuildernewStringBuilder().append(IT空门).append(·).append(门主).toString();// Stream BuilderStreamStringstreamStream.Stringbuilder().add(Java).add(Spring).add(AI).build();2. Lombok 的 BuilderSpring Boot 项目 100% 在用BuilderDatapublicclassUserDTO{privateStringname;privateIntegerage;privateStringemail;}UserDTOuserUserDTO.builder().name(门主).age(18).email(xxxxx.com).build();3. 主流框架的链式门面框架/工具Builder 场景OkHttpOkHttpClient.Builder()RetrofitRetrofit.Builder()Spring SecurityHttpSecurity配置MyBatis-PlusQueryWrapper条件构造器Elasticsearch Java Client各种*Request.Builder()Kafka ProducerKafkaProducer配置看到没Builder 模式是 Java 生态的通用语言。你不学它连框架的源码都看不懂。四、建造者模式到底解决了什么为什么非用不可痛点 1构造器参数爆炸Telescoping Constructor// 噩梦写法publicUser(Stringname){}publicUser(Stringname,intage){}publicUser(Stringname,intage,Stringemail){}publicUser(Stringname,intage,Stringemail,Stringphone){}// ...参数每加一个构造函数就翻倍调用时你根本不知道哪个参数是干嘛的newUser(张三,28,13800000000,北京,true);// 这 true 是啥痛点 2Setter 满天飞对象状态不完整UserusernewUser();user.setName(张三);// 忘了 setAgeservice.process(user);// NPE 在向你招手痛点 3不可变对象做不出来如果想搞 final 字段 无 setter 线程安全构造器是你唯一的选择。但参数一多构造器就变成意大利面条。✅ 建造者模式的解法UseruserUser.builder().name(张三).age(28).email(xxxxx.com).phone(13800000000).address(北京).vip(true).build();可读性 ✅ 不可变 ✅ 线程安全 ✅ 参数可扩展 ✅五、优缺点分析带避坑✅ 优点优点说明可读性爆表像读英语一样读代码解决构造器爆炸不用再写 N 个构造函数天然支持不可变配合final字段无敌参数可灵活组合必选/可选分得清清楚楚便于校验在build()里统一校验参数线程安全Builder 本身不是但构建出的对象是不可变的❌ 缺点缺点说明代码量多至少多一个内部类Lombok 解决小对象过度设计2~3 个字段就别装了Builder 复用性差一次构建完就废了别复用Builder 本身非线程安全千万别多线程共享一个 Builder调试时栈深链式调用多了debug 一层层进⚠️ 6 个最容易踩的坑实战血泪坑 1Builder 默认值失效// ❌ 错误默认值是 null不是 []BuilderpublicclassPageQuery{privateListStringtagsnewArrayList();// 失效}// ✅ 正确必须加 Builder.DefaultBuilderpublicclassPageQuery{Builder.DefaultprivateListStringtagsnewArrayList();}坑 2Builder 和 NoArgsConstructor 打架// ❌ 错误Builder 找不到构造器悄悄失效BuilderNoArgsConstructorpublicclassUser{}// ✅ 黄金组合三者齐上DTO 必备DataBuilderNoArgsConstructorAllArgsConstructorpublicclassUser{}坑 3父子类继承时 Builder 失效// ❌ 普通 Builder 不支持父类字段publicclassAnimal{privateStringspecies;}publicclassCatextendsAnimal{privateStringcolor;}// Cat.builder().species(猫).color(白) // 编译失败species 没有// ✅ 必须用 SuperBuilderLombok ≥ 1.18.12SuperBuilderpublicclassAnimal{privateStringspecies;}SuperBuilderpublicclassCatextendsAnimal{privateStringcolor;}坑 4Jackson 反序列化炸了// ❌ Builder 生成的对象Jackson 反序列化会找不到无参构造器BuilderpublicclassUser{}// ✅ 加上 JacksonizedLombok ≥ 1.18.14BuilderJacksonizedpublicclassUser{}坑 5Builder 复用导致幽灵字段BuilderUserbuilderUser.builder().name(张三);Useru1builder.age(18).build();Useru2builder.build();// ⚠️ u2 也带 age18复用 Builder 翻车结论一个 Builder 就 build 一次用完就扔。坑 6把 Builder 当 Setter 用乱序赋值// ❌ 把校验逻辑写在 setter 里对象还能处于半成品状态publicBuilderage(intage){this.ageage;returnthis;}// ✅ 校验统一放 build()拒绝半成品publicUserbuild(){if(age0||age150){thrownewIllegalArgumentException(年龄不合法);}if(StringUtils.isBlank(name)){thrownewIllegalArgumentException(姓名不能为空);}returnnewUser(this);}六、代码实战手写一个企业级 Builder别只盯着 Lombok手写一次才能真正理解原理。场景构造一个系统通知对象必选字段接收人通知类型可选字段标题内容跳转链接优先级附件列表1. 产品类Product/** * 系统通知不可变对象 * author IT空门 */publicfinalclassNotice{// 必选privatefinalStringreceiver;privatefinalNoticeTypetype;// 可选privatefinalStringtitle;privatefinalStringcontent;privatefinalStringurl;privatefinalPrioritypriority;privatefinalListStringattachments;// 私有构造只能通过 Builder 创建privateNotice(Builderbuilder){this.receiverbuilder.receiver;this.typebuilder.type;this.titlebuilder.title;this.contentbuilder.content;this.urlbuilder.url;this.prioritybuilder.priority;// 防御性拷贝防止外部修改影响内部状态this.attachmentsbuilder.attachmentsnull?Collections.emptyList():Collections.unmodifiableList(newArrayList(builder.attachments));}publicstaticBuilderbuilder(Stringreceiver,NoticeTypetype){returnnewBuilder(receiver,type);}// 省略 getter ...publicStringgetReceiver(){returnreceiver;}publicNoticeTypegetType(){returntype;}// ... 其他字段}2. 静态内部 BuilderpublicstaticfinalclassBuilder{// 必选privatefinalStringreceiver;privatefinalNoticeTypetype;// 可选带默认值privateStringtitle系统通知;privateStringcontent;privateStringurl;privatePrioritypriorityPriority.NORMAL;privateListStringattachments;// 必选字段强制通过构造器传入privateBuilder(Stringreceiver,NoticeTypetype){this.receiverreceiver;this.typetype;}publicBuildertitle(Stringtitle){this.titletitle;returnthis;}publicBuildercontent(Stringcontent){this.contentcontent;returnthis;}publicBuilderurl(Stringurl){this.urlurl;returnthis;}publicBuilderpriority(Prioritypriority){this.prioritypriority;returnthis;}publicBuilderattachment(Stringattachment){if(this.attachmentsnull){this.attachmentsnewArrayList();}this.attachments.add(attachment);returnthis;}/** * 终极构建统一校验 */publicNoticebuild(){// 1. 必填校验if(receivernull||receiver.isEmpty()){thrownewIllegalArgumentException(接收人不能为空);}if(typenull){thrownewIllegalArgumentException(通知类型不能为空);}// 2. 业务校验if(priorityPriority.URGENT(contentnull||content.length()5)){thrownewIllegalArgumentException(紧急通知内容不能少于 5 个字);}returnnewNotice(this);}}3. 客户端调用丝滑体验// 场景 1只填必选Noticen1Notice.builder(张三,NoticeType.SYSTEM).build();// 场景 2链式配置Noticen2Notice.builder(李四,NoticeType.MESSAGE).title(您的订单已发货).content(订单号: 20260112001, 顺丰快递).url(https://example.com/order/20260112001).priority(Priority.HIGH).attachment(https://cdn.example.com/快递单.jpg).attachment(https://cdn.example.com/签收码.png).build();// 场景 3紧急通知触发校验Noticen3Notice.builder(王五,NoticeType.ALERT).priority(Priority.URGENT).content(bug)// ⚠️ 触发业务校验.build();// 抛出紧急通知内容不能少于 5 个字看到没必选参数在 Builder 构造器里强制传少一个都编译不过可选参数想填哪个填哪个不填走默认值校验集中在build()杜绝半成品对象不可变 防御性拷贝保证线程安全七、建造者模式 vs 工厂模式别再傻傻分不清这是面试高频题一张表搞定对比维度建造者模式工厂模式关注点怎么一步步造出来造出哪个种类过程分步骤、链式一个方法搞定适合场景复杂对象、参数多对象种类多返回结果同一个产品不同表示不同产品类型典型例子OkHttpClient.BuilderCalendar.getInstance()一句话区分工厂模式你要什么我给你什么What建造者模式你要怎么造我一步步给你造How八、避坑速查表直接抄走场景推荐写法备注普通 DTO/VOData Builder NoArgsConstructor AllArgsConstructor四件套黄金组合实体类MyBatis-PlusData Builder NoArgsConstructor实体别加 AllArgsConstructor父子类继承SuperBuilder父类也加需要默认值Builder.Default别忘了它Jackson 序列化Builder JacksonizedLombok ≥ 1.18.14Spring 配置类别用 Builder用 ConfigurationProperties2~3 个字段别用 Builder过度设计校验逻辑写在build()里别散在 setter九、写在最后 拓展阅读建造者模式不难难的是有人愿意告诉你坑在哪。它不是什么高深莫测的设计模式而是 Java 工程师每天都在用、但不一定意识到的基建。记住三个关键词链式调用·不可变对象·build() 校验从今天开始看到 4 个以上参数的类别犹豫Builder 走起看到 2~3 个字段的类克制别装看到Builder旁边没配NoArgsConstructor心里默默帮他补上。技术不难难的是没人告诉你坑在哪。如果这篇帮到你点个赞顺便帮我抢救一下发际线 作者介绍写文不易Bug 更不易。如果这篇文章对你有帮助可以搜一搜空门技术栈这里分享✅ Java / Spring AI / 企业级项目实战✅ Docker / RAG知识库 / 微服务踩坑✅ Python、前端、AI应用落地✅ 偶尔分享一些「头发保卫战」经验 一个热爱技术、持续填坑的开发者陪你一起少踩坑少加班多写优雅代码。 推荐阅读Java 建造者模式详解从入门到实战附避坑指南AI 为什么总失忆LangChain Memory 完全指南从 InMemory 到 Redis 实战避坑Java 单例模式详解7 种实现方式 volatile 原理 反射与序列化问题告别手动复制接口文档Apifox MCP AI 自动测试让开发效率起飞别再纠结学哪个了Java AI 三大框架深度对比Spring AI vs AgentScope-JavaMySQL MCP Server 从零安装到使用实战AI 直接查询数据库Spring 注入三剑客Resource、Autowired、RequiredArgsConstructor 到底该用哪个Spring Event 用了三年同事一句话把我问懵了 技术交流 / 项目合作平时也会做一些技术项目与咨询包括Java / Spring Boot 企业级项目开发AI 应用开发LangChain、RAG、Agent、知识库Docker / Linux / 私有化部署系统功能开发、接口对接、性能优化疑难问题排查与技术咨询如果你想做 AI 项目但不确定技术方案项目卡在某个 Bug 很久想把 AI 接入现有系统需要企业级开发支持欢迎交流。联系方式Email2929119150qq.com也可以私信我技术交流可通过个人主页联系有些坑一个人踩是事故一起踩就是经验