建造者模式深度解析:从奶茶定制到代码构建的优雅之道
在软件开发中,我们经常需要创建具有多个属性的复杂对象。如果直接使用构造函数,参数列表会变得冗长且难以维护;而使用 setter 方法,又可能导致对象状态不完整。今天,我们来深入探讨一种优雅的解决方案——建造者模式(Builder Pattern),它通过分离构建过程与表示,让对象的创建变得清晰、灵活且安全。本文将结合 Java 示例,并融入 TypeScript、Go、C++、JavaScript、Python 等语言的实现思路,帮你彻底掌握这一模式。
一、建造者模式的核心定义
建造者模式是一种创建型设计模式,其核心思想是:将复杂对象的构建过程与它的表示分离,使得同样的构建流程可以创建出不同的对象实例。
通俗比喻:就像你去奶茶店点奶茶——构建一杯奶茶的“流程”是固定的(选基底、选甜度、选配料、加冰/热),但每一步的“具体选择”可以不同(比如基底选红茶/绿茶,甜度选三分/五分),最终能做出不同口味的奶茶。建造者模式就是把“流程(步骤)”和“具体实现(选择)”分开,让构建过程更清晰、灵活。
二、为什么需要建造者模式?
建造者模式主要解决以下问题:
- 避免“臃肿的构造函数”:当一个对象有很多属性(尤其是可选属性)时,直接用构造函数会导致参数列表过长(比如
new Computer("i9", 32, "1T", "4090", "Windows")),可读性差且易出错。在 JavaScript 或 TypeScript 中,类似问题同样存在。 - 保证对象状态统一:如果用
setter方法逐个设置属性,对象在构造过程中可能处于“不完整状态”(比如只设了 CPU 没设内存就被调用)。 - 解耦构建流程和细节:构建步骤固定,但每个步骤的实现可灵活替换,便于扩展。
✅ 优点:
- 分离构建过程和表示,构建过程更灵活,可以构建不同的表示。
- 更好地控制构建过程,隐藏具体构建细节。
- 代码复用性高,可以在不同的构建过程中重复使用相同的建造者。
⚠️ 缺点:
- 如果产品的属性较少,建造者模式可能会导致代码冗余。
- 增加了系统的类和对象数量。
在 Python 或 Go 中,虽然语言特性不同,但建造者模式的思想同样适用,可以通过类或结构体组合来实现。
三、建造者模式的核心角色
| 角色 | 作用 |
|---|---|
| 产品(Product) | 要构建的复杂对象(比如电脑、奶茶、汽车) |
| 抽象建造者(Builder) | 定义构建产品的所有步骤(比如 “设置 CPU”“设置内存”),通常是接口 / 抽象类 |
| 具体建造者(ConcreteBuilder) | 实现抽象建造者的方法,完成具体产品的构建(比如 “游戏本建造者”“办公本建造者”) |
| 指挥者(Director) | 可选角色,负责按固定顺序调用建造步骤,控制整体构建流程 |
这些角色共同协作,实现了构建过程与表示的分离。在简化版本中,我们通常省略 Director(导演)角色,直接使用静态内部 Builder 类,这在日常开发中最为常见。
四、代码示例(Java 实现)
以“构建电脑”为例,电脑有必选属性(CPU、内存)和可选属性(硬盘、显卡、系统),用建造者模式实现:
1. 产品类(Computer)
// 要构建的复杂对象:电脑
public class Computer {
// 必选属性
private String cpu;
private int ram;
// 可选属性
private String hardDisk;
private String graphicsCard;
private String os;
// 私有化构造方法,只能通过Builder创建
private Computer(Builder builder) {
this.cpu = builder.cpu;
this.ram = builder.ram;
this.hardDisk = builder.hardDisk;
this.graphicsCard = builder.graphicsCard;
this.os = builder.os;
}
// 静态内部建造者类(最常用的简化写法,省略Director)
public static class Builder {
// 必选属性(必须在Builder构造时传入)
private String cpu;
private int ram;
// 可选属性(默认值或按需设置)
private String hardDisk = "512G SSD"; // 默认硬盘
private String graphicsCard = "集成显卡"; // 默认显卡
private String os = "Windows 10"; // 默认系统
// Builder构造方法:强制传入必选属性
public Builder(String cpu, int ram) {
this.cpu = cpu;
this.ram = ram;
}
// 可选属性的设置方法,返回Builder本身(链式调用)
public Builder hardDisk(String hardDisk) {
this.hardDisk = hardDisk;
return this;
}
public Builder graphicsCard(String graphicsCard) {
this.graphicsCard = graphicsCard;
return this;
}
public Builder os(String os) {
this.os = os;
return this;
}
// 构建方法:创建Computer实例
public Computer build() {
// 可选:在这里做参数校验,确保对象完整
if (cpu == null || ram <= 0) {
throw new IllegalArgumentException("CPU和内存不能为空!");
}
return new Computer(this);
}
}
// 重写toString,方便查看结果
@Override
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", ram=" + ram + "G" +
", hardDisk='" + hardDisk + '\'' +
", graphicsCard='" + graphicsCard + '\'' +
", os='" + os + '\'' +
'}';
}
}2. 测试代码(使用建造者创建对象)
public class BuilderTest {
public static void main(String[] args) {
// 构建“办公本”:只用必选属性+默认可选属性
Computer officeComputer = new Computer.Builder("i5-13400", 16)
.build();
System.out.println("办公本:" + officeComputer);
// 构建“游戏本”:必选属性+自定义可选属性(链式调用)
Computer gameComputer = new Computer.Builder("i9-14900K", 32)
.hardDisk("2T SSD")
.graphicsCard("RTX 4090")
.os("Windows 11")
.build();
System.out.println("游戏本:" + gameComputer);
}
}3. 运行结果
办公本:Computer{cpu='i5-13400', ram=16G, hardDisk='512G SSD', graphicsCard='集成显卡', os='Windows 10'}
游戏本:Computer{cpu='i9-14900K', ram=32G, hardDisk='2T SSD', graphicsCard='RTX 4090', os='Windows 11'}在 C++ 或 TypeScript 中,实现思路类似,可以利用模板或泛型来增强类型安全。
五、代码关键说明
以下是建造者模式实现中的几个关键设计点:
- 私有化产品构造方法:确保只能通过 Builder 创建对象,避免对象状态不完整。
核心作用:禁止外部通过// 私有化构造方法,只能通过Builder创建Computer实例 private Computer(Builder builder) { this.cpu = builder.cpu; this.ram = builder.ram; this.hardDisk = builder.hardDisk; this.graphicsCard = builder.graphicsCard; this.os = builder.os; }new Computer()直接创建对象,必须通过内部的Builder类来创建,保证对象创建的可控性。赋值逻辑:从Builder对象中读取配置好的属性值,赋值给Computer的属性(Builder 相当于“配置器”)。 - 静态内部 Builder 类:这是最常用的简化写法(省略 Director),符合“迪米特法则”。
- Builder 的构造方法(强制传入必选属性):
核心作用:强制要求创建 Builder 时必须传入 CPU 和内存,从源头保证必选属性不缺失。举例:如果写// Builder构造方法:强制传入必选属性 public Builder(String cpu, int ram) { this.cpu = cpu; this.ram = ram; }new Computer.Builder()会直接编译报错,必须写new Computer.Builder("i5", 16)。 - 链式调用:每个可选属性的设置方法返回
this,让代码更简洁(builder.xx().yy().build())。
核心设计:每个方法执行完属性赋值后,返回// 可选属性的设置方法,返回Builder本身(链式调用) public Builder hardDisk(String hardDisk) { this.hardDisk = hardDisk; return this; } public Builder graphicsCard(String graphicsCard) { this.graphicsCard = graphicsCard; return this; } public Builder os(String os) { this.os = os; return this; }this(当前 Builder 对象),这是“链式调用”的关键。效果:可以写builder.hardDisk("2T SSD").graphicsCard("RTX4090").os("Windows11"),代码简洁且可读性高。对比:如果不返回this,需要写builder.hardDisk("2T SSD"); builder.graphicsCard("RTX4090");,代码冗余。 - 参数校验:在
build()方法中校验必选属性,保证对象合法性。
核心作用:触发// 构建方法:创建Computer实例 public Computer build() { // 可选:在这里做参数校验,确保对象完整 if (cpu == null || ram <= 0) { throw new IllegalArgumentException("CPU和内存不能为空!"); } return new Computer(this); }Computer对象的创建,是建造者模式的“收尾步骤”。参数校验:额外增加合法性检查(比如 CPU 不能为 null、内存不能≤0),即使通过了 Builder 构造方法,也能防止传入非法值(比如new Builder(null, -8))。异常类型:IllegalArgumentException是 Java 标准异常,用于表示“参数不合法”,符合语义。
在 TypeScript 或 Python 中,可以通过类型注解或装饰器实现类似的校验逻辑。
六、建造者模式的实际应用场景
建造者模式在现实开发中应用广泛,以下是一些典型场景:
- SDK / 框架设计:比如 Java 的
StringBuilder(本质是简化的建造者模式,构建复杂字符串)、MyBatis 的SqlSessionFactoryBuilder。在 JavaScript 中,类似 jQuery 的链式调用也体现了建造者思想。 - 复杂对象创建:比如创建订单(包含收货人、商品、支付方式、物流等多属性)、配置对象(比如连接池配置)。
- 生成器工具:比如 JSON/XML 生成器(固定的构建步骤,不同内容对应不同表示)。
[AFFILIATE_SLOT_1]
七、总结
建造者模式的核心是分离“构建步骤”和“具体实现”,解决复杂对象的创建问题。最常用的形式是静态内部 Builder 类 + 链式调用,无需单独的 Director 角色,简洁易用。它的核心价值是保证对象创建的灵活性和安全性,避免臃肿构造函数和不完整对象。
八、扩展
一、带 Director 角色的完整建造者模式(Java 实现)
带 Director 的版本更贴合建造者模式的经典定义,Director 负责“把控构建流程”,具体建造者负责“实现构建细节”,完全解耦流程和实现。依然以“构建电脑”为例:
1. 产品类(Product):Computer
// 要构建的复杂对象:电脑(和之前一致,仅调整访问权限适配Builder)
public class Computer {
private String cpu;
private int ram;
private String hardDisk;
private String graphicsCard;
private String os;
// 空构造(让Builder可以设置属性)
public Computer() {}
// setter方法(供Builder调用)
public void setCpu(String cpu) {
this.cpu = cpu;
}
public void setRam(int ram) {
this.ram = ram;
}
public void setHardDisk(String hardDisk) {
this.hardDisk = hardDisk;
}
public void setGraphicsCard(String graphicsCard) {
this.graphicsCard = graphicsCard;
}
public void setOs(String os) {
this.os = os;
}
@Override
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", ram=" + ram + "G" +
", hardDisk='" + hardDisk + '\'' +
", graphicsCard='" + graphicsCard + '\'' +
", os='" + os + '\'' +
'}';
}
}2. 抽象建造者(Builder):ComputerBuilder
定义构建电脑的所有核心步骤,是具体建造者的“规范”:
// 抽象建造者:定义构建电脑的所有步骤
public abstract class ComputerBuilder {
// 持有产品对象,供子类设置属性
protected Computer computer = new Computer();
// 必选构建步骤(抽象方法,子类必须实现)
public abstract void buildCpu();
public abstract void buildRam();
// 可选构建步骤(默认实现,子类可重写)
public void buildHardDisk() {
computer.setHardDisk("512G SSD"); // 默认值
}
public void buildGraphicsCard() {
computer.setGraphicsCard("集成显卡"); // 默认值
}
public void buildOs() {
computer.setOs("Windows 10"); // 默认值
}
// 构建完成,返回产品
public Computer getComputer() {
return computer;
}
}3. 具体建造者(ConcreteBuilder)
实现抽象建造者的步骤,完成“游戏本”和“办公本”的具体构建:
// 具体建造者1:游戏本建造者
public class GameComputerBuilder extends ComputerBuilder {
@Override
public void buildCpu() {
computer.setCpu("i9-14900K"); // 游戏本高性能CPU
}
@Override
public void buildRam() {
computer.setRam(32); // 游戏本32G内存
}
// 重写可选步骤,自定义游戏本配置
@Override
public void buildHardDisk() {
computer.setHardDisk("2T SSD");
}
@Override
public void buildGraphicsCard() {
computer.setGraphicsCard("RTX 4090");
}
@Override
public void buildOs() {
computer.setOs("Windows 11");
}
}
// 具体建造者2:办公本建造者
public class OfficeComputerBuilder extends ComputerBuilder {
@Override
public void buildCpu() {
computer.setCpu("i5-13400"); // 办公本中等CPU
}
@Override
public void buildRam() {
computer.setRam(16); // 办公本16G内存
}
// 可选步骤用默认值,无需重写
}4. 指挥者(Director)
负责固定构建流程,调用建造者的步骤,控制“先建什么、后建什么”:
// 指挥者:控制电脑的构建流程
public class ComputerDirector {
// 构建方法:传入具体建造者,按固定流程构建
public Computer constructComputer(ComputerBuilder builder) {
// 固定构建顺序:CPU → 内存 → 硬盘 → 显卡 → 系统
builder.buildCpu();
builder.buildRam();
builder.buildHardDisk();
builder.buildGraphicsCard();
builder.buildOs();
return builder.getComputer();
}
}5. 测试代码(使用 Director 构建对象)
public class DirectorBuilderTest {
public static void main(String[] args) {
// 1. 创建指挥者
ComputerDirector director = new ComputerDirector();
// 2. 构建游戏本:传入游戏本建造者
ComputerBuilder gameBuilder = new GameComputerBuilder();
Computer gameComputer = director.constructComputer(gameBuilder);
System.out.println("游戏本:" + gameComputer);
// 3. 构建办公本:传入办公本建造者
ComputerBuilder officeBuilder = new OfficeComputerBuilder();
Computer officeComputer = director.constructComputer(officeBuilder);
System.out.println("办公本:" + officeComputer);
}
}6. 运行结果
游戏本:Computer{cpu='i9-14900K', ram=32G, hardDisk='2T SSD', graphicsCard='RTX 4090', os='Windows 11'}
办公本:Computer{cpu='i5-13400', ram=16G, hardDisk='512G SSD', graphicsCard='集成显卡', os='Windows 10'}关键说明(Director 的作用):
- Director 是“流程指挥官”:它定义了“先建 CPU、再建内存、最后建系统”的固定流程,不管传入哪个具体建造者,都按这个流程构建。
- 具体建造者是“细节执行者”:只负责实现每个步骤的具体内容(比如游戏本用 i9,办公本用 i5)。
- 扩展灵活:如果要新增“轻薄本”,只需新增
LightComputerBuilder,无需修改 Director 和其他代码(符合开闭原则)。
在 Go 或 C++ 中,可以通过接口或抽象类实现类似的 Director 模式。
二、建造者模式 vs 工厂模式(核心区别)
很多新手会混淆这两种创建型模式,核心差异在于“创建的关注点”和“使用场景”:
| 维度 | 建造者模式 | 工厂模式(简单工厂 / 工厂方法 / 抽象工厂) |
|---|---|---|
| 核心目的 | 构建复杂对象(多属性、多步骤),控制构建流程 | 创建简单 / 复杂对象,快速获取实例 |
| 创建方式 | 分步骤构建(一步步设置属性 / 组件),最后组装成对象 | 一次性创建(调用方法直接返回完整对象) |
| 关注点 | 关注 “如何构建”(步骤、流程、细节) | 关注 “创建谁”(选择哪个对象实例) |
| 对象属性灵活性 | 高:可自定义多个可选属性,支持链式配置 | 低:通常返回固定配置的对象 |
| 代码特征 | 有明确的 “步骤方法”+“build/construct” 方法 | 有明确的 “工厂方法”(如) |
| 通俗比喻 | 拼乐高:按步骤拼出不同造型(流程固定,零件可选) | 去便利店买水:说 “买矿泉水” 直接拿到,不用管怎么生产 |
| 典型使用场景 | 1. 多属性对象(订单、配置、电脑)2. 需控制构建流程3. 避免臃肿构造函数 | 1. 快速创建单一类型对象(如)2. 解耦对象创建和使用3. 统一对象创建入口 |
| 典型例子 | 、MyBatis 的、OkHttp 的 | 、Spring 的、简单工厂创建图形 |
补充:何时选建造者,何时选工厂?
- 如果你需要自定义对象的多个属性(比如电脑的 CPU、内存、显卡都要选)→ 用建造者模式。
- 如果你只需要获取一个固定类型的对象(比如“我要一个圆形”“我要一个 MySQL 连接池”)→ 用工厂模式。
- 建造者模式是“定制化生产”,工厂模式是“标准化量产”。
[AFFILIATE_SLOT_2]
总结
带 Director 的建造者模式:通过“抽象 Builder 定步骤、具体 Builder 做实现、Director 控流程”,完全解耦构建流程和细节,适合需要统一构建流程的场景。简化版建造者(静态内部类):省略 Director,通过链式调用直接构建,更简洁,日常开发中使用更广。建造者 vs 工厂:核心差异是“分步骤定制构建” vs “一次性标准化创建”,前者关注“怎么建”,后者关注“建什么”。

createXXX()ShapeFactory.createCircle()StringBuilderSqlSessionFactoryBuilderRequest.BuilderCalendar.getInstance()BeanFactory
浙公网安备 33010602011771号