Java设计模式-建造者模式
模式概述
建造者模式简介
定义:
它允许逐步构建复杂对象而不至于构造函数变得庞大和难以管理。
将复杂对象的构建过程与它的表示分离,使得同样的构建过程可以创建不同的表示。通过定义一个独立的“建造者”角色,将对象的构造逻辑封装起来,客户端只需调用建造者的方法分步构建对象,最终生成完整的产品。
模式类型:创建型模式
作用:
- 解耦对象的构建过程与最终表示,提高代码的可维护性和扩展性;
- 灵活支持复杂对象的多种配置组合(如可选参数的动态设置);
- 避免“伸缩构造函数”问题(Telescoping Constructor Problem),即当对象属性过多时,构造方法参数列表过长导致的可读性差和错误风险;
- 支持生成不可变对象(Immutable Object),通过建造者完成所有属性设置后,对象状态不再修改。
典型应用场景:
- 对象包含多个组成部分,且需要灵活组合不同配置(如汽车的发动机、底盘、座椅等部件);
- 对象需要支持多种可选参数(如HTTP请求的URL、方法、头信息、超时时间等);
- 需要生成不可变对象(如Java中的
String
、LocalDateTime
,或自定义的配置类); - 构建过程需要统一的流程控制(如必须按顺序初始化某些属性)。
我认为:建造者模式就像一个“产品组装流水线”,通过标准化的步骤组装零件(属性),最终产出不同配置的“产品”(对象),既保证了流程的规范性,又提供了灵活的组合方式。
课程目标
- 理解建造者模式的核心思想和经典应用场景
- 识别应用场景,使用建造者模式解决功能要求
- 了解建造者模式的优缺点
核心组件
角色-职责表
角色 | 职责 | 示例类名 |
---|---|---|
Director | 使用Builder 接口来构建产品,控制对象的构建流程。在某些场景下可以省略,特别是在简单的建造者模式应用中。 |
ComputerDirector |
Builder | 定义用于创建各个部分的方法,确保必填参数被设置,并允许设置可选参数。 | Computer.Builder |
ConcreteBuilder | 提供特定实现以构建并装配产品的具体部分。负责初始化对象的不同部分,并提供一个方法返回最终组装好的对象。 | DefaultBuilder |
Product | 被构建的复杂对象,即通过建造者模式最终生成的对象。该对象通常包含多个属性,其中一些可能是必需的,而另一些是可选的。 | Computer |
类图
传统实现 VS 建造者模式
案例需求
案例背景:构建一台电脑(Computer
),需要设置CPU、内存、硬盘三个核心属性。其中,CPU和内存为必填项,硬盘为可选(默认值为512GB SSD)。
传统实现(痛点版)
代码实现:
// 传统写法:通过构造方法和Setter构建对象
public class Computer {
private final String cpu; // 必填
private final String memory; // 必填
private String disk; // 可选(默认512GB SSD)
// 构造方法1:仅必填参数
public Computer(String cpu, String memory) {
this.cpu = cpu;
this.memory = memory;
this.disk = "512GB SSD"; // 默认值
}
// 构造方法2:必填+可选参数(参数列表膨胀)
public Computer(String cpu, String memory, String disk) {
this.cpu = cpu;
this.memory = memory;
this.disk = disk;
}
// Setter方法(可能导致对象状态不一致)
public void setDisk(String disk) {
this.disk = disk;
}
@Override
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", memory='" + memory + '\'' +
", disk='" + disk + '\'' +
'}';
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
// 场景1:使用必填参数+默认硬盘
Computer officePC = new Computer("Intel i5", "16GB");
// 场景2:自定义硬盘(需显式调用构造方法)
Computer gamingPC = new Computer("AMD R9", "32GB", "2TB HDD");
// 风险:若忘记设置必填参数(如cpu为null),对象状态非法
// Computer invalidPC = new Computer(null, "16GB");
// 风险:通过Setter修改必填参数(破坏不可变性)
officePC.setDisk("1TB SSD");
}
}
痛点总结:
- 参数列表膨胀:当可选参数增多时(如新增显卡、电源等),需要定义大量重载构造方法,可读性差且易出错;
- 状态不一致:通过Setter方法设置可选参数时,可能遗漏必填参数(如
cpu
为null
),或错误修改已设置的必填参数(破坏对象不可变性); - 构建逻辑分散:对象的构建过程(如必填参数校验、默认值设置)分散在构造方法和Setter中,难以统一维护。
建造者模式 实现(优雅版)
代码实现:
// 1. 产品类(Computer)
public class Computer {
private final String cpu; // 必填
private final String memory; // 必填
private final String disk; // 可选
// 私有构造方法,仅允许建造者创建对象
private Computer(Builder builder) {
this.cpu = builder.cpu;
this.memory = builder.memory;
this.disk = builder.disk;
}
@Override
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", memory='" + memory + '\'' +
", disk='" + disk + '\'' +
'}';
}
// 2. 抽象建造者(Builder接口)
public interface Builder {
Builder buildCpu(String cpu); // 必填参数
Builder buildMemory(String memory); // 必填参数
Builder buildDisk(String disk); // 可选参数
Computer build(); // 最终构建产品
}
// 3. 具体建造者(实现Builder接口)
public static class DefaultBuilder implements Builder {
private String cpu; // 必填
private String memory; // 必填
private String disk = "512GB SSD"; // 可选(默认值)
@Override
public Builder buildCpu(String cpu) {
if (cpu == null || cpu.isEmpty()) {
throw new IllegalArgumentException("CPU不能为空");
}
this.cpu = cpu;
return this; // 返回this支持链式调用
}
@Override
public Builder buildMemory(String memory) {
if (memory == null || memory.isEmpty()) {
throw new IllegalArgumentException("内存不能为空");
}
this.memory = memory;
return this;
}
@Override
public Builder buildDisk(String disk) {
if (disk != null && !disk.isEmpty()) {
this.disk = disk;
}
return this;
}
@Override
public Computer build() {
// 校验必填参数是否设置
if (cpu == null || memory == null) {
throw new IllegalStateException("CPU和内存为必填参数");
}
return new Computer(this);
}
}
// 4. 指挥者(可选,用于控制标准构建流程)
public static class Director {
private final Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
// 标准构建流程:先设置CPU和内存,再设置硬盘(可选)
public Computer constructStandardComputer(String cpu, String memory) {
return builder.buildCpu(cpu)
.buildMemory(memory)
.buildDisk() // 使用默认硬盘
.build();
}
public Computer constructGamingComputer(String cpu, String memory, String disk) {
return builder.buildCpu(cpu)
.buildMemory(memory)
.buildDisk(disk)
.build();
}
}
// 客户端使用
public static void main(String[] args) {
// 创建建造者
Builder builder = new DefaultBuilder();
// 方式1:直接通过建造者链式调用(无需指挥者)
Computer officePC = builder.buildCpu("Intel i5")
.buildMemory("16GB")
.buildDisk() // 使用默认值
.build();
// 方式2:通过指挥者执行标准流程
Director director = new Director(builder);
Computer gamingPC = director.constructGamingComputer("AMD R9", "32GB", "2TB HDD");
System.out.println(officePC); // Computer{cpu='Intel i5', memory='16GB', disk='512GB SSD'}
System.out.println(gamingPC); // Computer{cpu='AMD R9', memory='32GB', disk='2TB HDD'}
}
}
优势:
- 参数安全:必填参数通过建造者的方法强制设置(如
buildCpu()
校验非空),避免无效对象; - 灵活组合:可选参数通过方法链式调用(
buildDisk()
)设置,默认值封装在建造者内部,客户端无需关心; - 不可变性支持:产品对象的属性通过私有构造方法初始化后不可修改(
final
修饰),线程安全; - 流程可控:指挥者可定义标准构建流程(如必须先设置CPU和内存),避免构建步骤混乱;
- 代码整洁:构建逻辑集中在建造者中,客户端只需调用简洁的链式方法,可读性高。
局限:
- 类数量增加:需要定义产品类、抽象建造者、具体建造者和可能的指挥者,若产品简单(如仅1-2个属性),会导致过度设计;
- 学习成本:对于新手开发者,需要理解建造者模式的角色分工,增加学习曲线。
模式变体
- 简化版(无指挥者):客户端直接调用建造者的方法完成构建,省略指挥者角色(如上述案例中的
DefaultBuilder
直接被客户端调用); - 链式调用:建造者的方法返回
this
,支持流畅的方法链(如builder.buildCpu().buildMemory().build()
); - 不可变产品:产品对象的所有属性通过构造方法初始化后设为
final
,且不提供setter
方法,确保线程安全; - Lombok
@Builder
:通过Lombok注解自动生成建造者代码(如@Builder(toBuilder = true)
),减少手动编写样板代码; - 多阶段构建:建造者支持分阶段构建(如先构建基础属性,再构建高级属性),适用于复杂对象的渐进式初始化。
最佳实践
建议 | |
---|---|
优先用于多属性/可选参数对象 | 当对象包含3个及以上属性(尤其是可选参数)时,建造者模式能显著提升代码可读性和可维护性。 |
必填参数在构造方法中校验 | 建造者的方法(如buildCpu() )应对必填参数进行非空/格式校验,避免无效对象被创建。 |
使用链式调用提升流畅度 | 建造者方法返回this ,支持builder.method1().method2() 的链式调用,代码更简洁。 |
产品属性设为**final ** |
确保产品对象在build() 后不可修改,支持线程安全和不可变对象特性。 |
指挥者封装标准流程 | 若构建过程有固定顺序(如必须先初始化数据库连接再配置查询参数),通过指挥者统一控制流程。 |
避免过度设计 | 若对象仅需1-2个简单属性,直接使用构造方法或Setter更合适,无需引入建造者模式。 |
一句话总结
建造者模式通过分离对象的构建过程与表示,解决了复杂对象创建时的参数管理和灵活性问题,使代码更易维护、扩展,并支持生成安全的不可变对象。
如果关注Java设计模式内容,可以查阅作者的其他Java设计模式系列文章。😊