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

📅 2026/6/23 19:20:36
设计模式——建造者模式模式(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 风格大量 setterPizza 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 MapString, 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 MapString, 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{ methodGET urlhttps://api.example.com/users headers{AuthorizationBearer token123} bodynull timeout5000ms followRedirectstrue } HttpRequest{ methodPOST urlhttps://api.example.com/users headers{Content-Typeapplication/json, AuthorizationBearer token123} body{name: 张三, age: 28} timeout8000ms followRedirectsfalse } 捕获异常: POST 请求必须提供 body建造者的三个关键设计决策必填参数放构造器选填参数放链式方法// 必填放构造器缺了连 Builder 都建不出来 new Builder(POST, https://...) // 选填放链式方法不调就用默认值 .timeoutMs(5000) .body(...)这比把所有参数都堆进build()要好IDE 会直接提示你哪些字段必须填。校验逻辑放build()而不是产品类build()是构造的最后一步此时所有参数都已收集完毕可以做跨字段的整体校验比如POST 必须有 body这种单字段校验做不到的约束。产品类的构造器只负责赋值保持简单。产品类字段全部final通过 Builder 构造出来的对象是完整的、不可变的构造完成后没有任何方式能修改它的状态天然线程安全也避免了对象被意外篡改。优缺点优点链式调用可读性强每个参数都有名字一眼看清在配置什么必填参数通过构造器强制保证选填参数有合理默认值build()时集中校验构造失败在最早的时间点抛出产品对象可以是完全不可变的缺点代码量翻倍——每个需要 Builder 的类都要维护一个镜像结构的内部类参数极少23个且全部必填时完全不必要直接构造器更简洁如果产品类字段很多Builder 类本身也会变得很长适用场景判断参数超过 4 个、或者有选填参数、或者参数之间有约束关系就值得引入 Builder。Java 生态里OkHttpClient、Retrofit、AlertDialogAndroid、ProcessBuilder都是经典的 Builder 应用。Lombok 的Builder注解可以自动生成这套代码实际项目里能省不少样板代码。