当前位置: 首页 > news >正文

设计模式——建造者模式模式(AI回答)

请详细解释建造者模式(Builder)的思路、优缺点和代码示例

建造者模式(Builder)

核心思路

建造者模式解决的是复杂对象的构造问题:当一个对象有很多参数,其中一些必填、一些选填、一些之间还有约束关系时,直接用构造器或一堆 setter 会让调用方很痛苦。

来看问题是怎么出现的。


没有建造者时的两种困境

困境一:伸缩构造器(Telescoping Constructor)

为了应对不同的参数组合,写一堆重载构造器:

public class Pizza { public Pizza(String size) { ... } public Pizza(String size, boolean cheese) { ... } public Pizza(String size, boolean cheese, boolean pepperoni) { ... } public Pizza(String size, boolean cheese, boolean pepperoni, boolean mushroom) { ... } // 继续加参数?继续加重载... } // 调用方完全看不懂这串 true/false 是什么意思 Pizza p = new Pizza("large", true, false, true);

困境二:JavaBean 风格(大量 setter)

Pizza p = new Pizza(); p.setSize("large"); p.setCheese(true); p.setPepperoni(false); // 问题:对象在一堆 setter 调用完之前处于"半构造"状态 // 无法在构造完成时做参数校验,也无法做成不可变对象

两种方式都有缺陷:前者可读性差,后者对象状态不安全。建造者模式同时解决这两个问题。


Java 代码示例

以构建一个 HTTP 请求对象为例,参数多且可选,是建造者的典型场景。

产品类(不可变对象)

public class HttpRequest { // 必填 private final String method; private final String url; // 选填 private final Map<String, String> headers; private final String body; private final int timeoutMs; private final boolean followRedirects; // 构造器私有,只能通过 Builder 创建 private HttpRequest(Builder builder) { this.method = builder.method; this.url = builder.url; this.headers = Collections.unmodifiableMap(builder.headers); this.body = builder.body; this.timeoutMs = builder.timeoutMs; this.followRedirects = builder.followRedirects; } @Override public String toString() { return String.format( "HttpRequest{\n method=%s\n url=%s\n headers=%s\n" + " body=%s\n timeout=%dms\n followRedirects=%b\n}", method, url, headers, body, timeoutMs, followRedirects); } // ── Builder 作为静态内部类 ────────────────────────────── public static class Builder { // 必填字段 private final String method; private final String url; // 选填字段,带默认值 private Map<String, String> headers = new HashMap<>(); private String body = null; private int timeoutMs = 3000; private boolean followRedirects = true; // 必填参数通过构造器传入,保证不可缺少 public Builder(String method, String url) { if (method == null || url == null) throw new IllegalArgumentException("method 和 url 不能为空"); this.method = method; this.url = url; } // 每个 setter 返回 this,支持链式调用 public Builder header(String key, String value) { this.headers.put(key, value); return this; } public Builder body(String body) { this.body = body; return this; } public Builder timeoutMs(int timeoutMs) { if (timeoutMs <= 0) throw new IllegalArgumentException("timeout 必须大于 0"); this.timeoutMs = timeoutMs; return this; } public Builder followRedirects(boolean follow) { this.followRedirects = follow; return this; } // 最终构建,可在此做整体校验 public HttpRequest build() { if ("POST".equals(method) && body == null) throw new IllegalStateException("POST 请求必须提供 body"); return new HttpRequest(this); } } }

调用方

public class Main { public static void main(String[] args) { // 简单 GET 请求,只填必要参数 HttpRequest get = new HttpRequest.Builder("GET", "https://api.example.com/users") .header("Authorization", "Bearer token123") .timeoutMs(5000) .build(); // 完整 POST 请求 HttpRequest post = new HttpRequest.Builder("POST", "https://api.example.com/users") .header("Content-Type", "application/json") .header("Authorization", "Bearer token123") .body("{\"name\": \"张三\", \"age\": 28}") .timeoutMs(8000) .followRedirects(false) .build(); System.out.println(get); System.out.println(post); // 校验生效:POST 不传 body 会抛异常 try { new HttpRequest.Builder("POST", "https://api.example.com/users").build(); } catch (IllegalStateException e) { System.out.println("捕获异常: " + e.getMessage()); } } }

输出:

HttpRequest{ method=GET url=https://api.example.com/users headers={Authorization=Bearer token123} body=null timeout=5000ms followRedirects=true } HttpRequest{ method=POST url=https://api.example.com/users headers={Content-Type=application/json, Authorization=Bearer token123} body={"name": "张三", "age": 28} timeout=8000ms followRedirects=false } 捕获异常: POST 请求必须提供 body

建造者的三个关键设计决策

必填参数放构造器,选填参数放链式方法

// 必填:放构造器,缺了连 Builder 都建不出来 new Builder("POST", "https://...") // 选填:放链式方法,不调就用默认值 .timeoutMs(5000) .body("...")

这比把所有参数都堆进build()要好,IDE 会直接提示你哪些字段必须填。

校验逻辑放build()而不是产品类

build()是构造的最后一步,此时所有参数都已收集完毕,可以做跨字段的整体校验(比如"POST 必须有 body"这种单字段校验做不到的约束)。产品类的构造器只负责赋值,保持简单。

产品类字段全部final

通过 Builder 构造出来的对象是完整的、不可变的,构造完成后没有任何方式能修改它的状态,天然线程安全,也避免了对象被意外篡改。


优缺点

优点:

  • 链式调用可读性强,每个参数都有名字,一眼看清在配置什么
  • 必填参数通过构造器强制保证,选填参数有合理默认值
  • build()时集中校验,构造失败在最早的时间点抛出
  • 产品对象可以是完全不可变的

缺点:

  • 代码量翻倍——每个需要 Builder 的类都要维护一个镜像结构的内部类
  • 参数极少(2~3个且全部必填)时完全不必要,直接构造器更简洁
  • 如果产品类字段很多,Builder 类本身也会变得很长

适用场景判断

参数超过 4 个、或者有选填参数、或者参数之间有约束关系,就值得引入 Builder。Java 生态里OkHttpClientRetrofitAlertDialog(Android)、ProcessBuilder都是经典的 Builder 应用。Lombok 的@Builder注解可以自动生成这套代码,实际项目里能省不少样板代码。

http://www.jsqmd.com/news/1068908/

相关文章:

  • 一行代码看懂 Linux 内核的时间转换:__month_to_secs 逐行拆解
  • 【2026奇点大会官方技术白皮书】:首次公开AI原生微调5大核心范式与3类失效场景避坑指南
  • 2026金九银十Java八股文面试题汇总(附答案·全栈覆盖)
  • 古法革新赋能民生 非遗医术焕新赋能大健康——李章武以科创思维激活中医外治新活力
  • 彻底搞懂 musl libc 的 __secs_to_tm:时间戳转 struct tm 的极致优化
  • 多色流式无串扰!647细胞凋亡检测试剂盒
  • 服装布料批量裁剪,CO2 激光高速裁切
  • 线上培训平台排名参考,不同场景选型指南
  • 行业内口碑顶尖!这3家推拉力测试机供应商为何备受信赖?
  • 水凝胶微针基底液:成型稳不稳,关键看这一步
  • 浙大、腾讯团队提出 JAVEdit 相关成果,填补自然语言驱动联合音视频编辑空白
  • 【港教育科技人才创新发展研究院主办 | ACM ICPS,稳定EI检索 | 议题涵盖大模型架构优化、多模态学习、产业落地等关键领域】2026年大模型技术与应用国际会议(LMTA 2026)
  • 基于MATLAB的直流无刷电机速度控制附Simulink仿真
  • 淘宝图片下载工具技术路线深度解析:从爬虫到浏览器的完整技术演进与选型指南
  • 怎样科学评估营销活动真实声量?智能清洗假粉留言的实用指南
  • 幽默,一个 Github 名字叫“马尾辫”,但是他给你省了 80% 的 token
  • Mistral AI:企业控制 AI 层的新希望,能否在巨头林立的市场突围?
  • 腾讯地图AI功能实测与开发避坑指南
  • hadop和flink有什么关系,他们分别有什么作用
  • 云手机 RESTful API 自动化开发实战:批量设备管理与远程指令下发
  • MyFramework:异步加载回调为什么要先转移再执行
  • 为什么你的RAG+CoT系统上线即崩?3个被92%团队忽略的时序一致性陷阱与实时修复补丁
  • 2026年薪酬设计:这3招让企业员工都满意
  • Spring AI MCP 工具调用测试文章
  • 深圳企业家怎么做个人IP?别再跟风唱跳,这套“工厂思维”才是爆款底层密码
  • 奈飞Netflix高级会员解锁版破解版 全网同步 终身免费使用观看
  • DSPE-PEG2000-NGR 靶向磷脂结构与两亲特性
  • 路侧单元被劫持,交叉路口的车全部收到了假信号——V2X路侧安全该怎么做?
  • AI芯漫平台本金减损措施正式出台,您可以申请本金核定
  • 哪些AI短剧的工具好用?2026全品类AI短剧创作工具盘点