在软件开发中,我们经常需要创建一些包含多个组成部分的复杂对象。比如一台电脑由 CPU、内存、硬盘、显卡等部件组成;一份简历包含基本信息、教育经历、工作经历、项目经验等模块。如果直接通过构造函数或 setter 方法来组装这些对象,不仅会导致代码臃肿、参数混乱,还难以灵活应对不同组合的需求。
今天我们来聊聊一种专门解决这类问题的设计模式 ------建造者模式,看看它如何像 "搭积木" 一样优雅地构建复杂对象。
一、什么是建造者模式
建造者模式的核心思想是:将一个复杂对象的构建过程与它的表示分离,使得同样的构建过程可以创建出不同的表示。
简单来说,就是把 "怎么组装零件" 和 "最终组装出什么产品" 分开。比如同样是组装电脑,"先装 CPU 再装内存最后装硬盘" 这个流程可以不变,但可以根据需求组装出 "游戏本"或 "办公本"。
二、建造者模式解决什么问题
假设我们要创建一个Computer类,如果用传统方式创建,可能会写出这样的代码:
java
// 传统方式:构造函数参数爆炸
public class Computer {
public Computer(String cpu, String memory, String hardDisk, String gpu, String os) {
// 初始化逻辑
}
}
// 使用时需要记住参数顺序,新增参数还要改构造函数
Computer gamePC = new Computer("i9-13900K", "32GB", "1TB SSD", "RTX 4090", "Windows 11");
这种方式的问题很明显:
- 参数过多,容易传错顺序;
- 如果需要创建不同配置(比如办公本不需要独立显卡),得重载多个构造函数,代码冗余;
- 构建过程(比如 "必须先装 CPU 才能装内存")无法控制,容易出现无效对象。
而建造者模式就能完美解决这些问题:它将构建步骤拆分,通过 "一步步设置" 的方式组装对象,同时隐藏复杂的构建逻辑。
三、建造者模式的核心角色
建造者模式包含 4 个核心角色,我们用 "组装电脑" 的例子来理解:
-
产品 :被构建的复杂对象(比如
Computer),包含多个组成部分。 -
抽象建造者:定义构建产品的抽象步骤(比如 "装 CPU""装内存"),以及返回最终产品的方法。它不关心具体怎么实现,只规定 "要做哪些事"。
-
具体建造者 :实现抽象建造者的步骤,负责具体的零件组装(比如
GameComputerBuilder实现 "装高性能 CPU""装独立显卡")。 -
指挥者:负责控制构建流程(比如 "先装 CPU→再装内存→最后装系统"),它调用具体建造者的方法,按照固定流程组装产品,用户不需要关心步骤细节。
四、代码实现:用建造者模式组装电脑
我们通过代码来实现 "组装电脑" 的例子,看看建造者模式如何落地。
1. 定义产品
首先是被构建的对象Computer,它包含多个部件:
java
// 产品:电脑
public class Computer {
private String cpu; // CPU
private String memory; // 内存
private String hardDisk; // 硬盘
private String gpu; // 显卡
private String os; // 操作系统
// 私有构造函数,避免外部直接创建
private Computer() {}
// getter方法(便于查看结果)
public String getCpu() { return cpu; }
public String getMemory() { return memory; }
public String getHardDisk() { return hardDisk; }
public String getGpu() { return gpu; }
public String getOs() { return os; }
// 内部静态类:用于修改私有属性(建造者需要访问)
public static class Builder {
private Computer computer;
public Builder() {
computer = new Computer();
}
// 逐步设置属性的方法(返回Builder本身,支持链式调用)
public Builder setCpu(String cpu) {
computer.cpu = cpu;
return this;
}
public Builder setMemory(String memory) {
computer.memory = memory;
return this;
}
public Builder setHardDisk(String hardDisk) {
computer.hardDisk = hardDisk;
return this;
}
public Builder setGpu(String gpu) {
computer.gpu = gpu;
return this;
}
public Builder setOs(String os) {
computer.os = os;
return this;
}
// 返回最终构建的产品
public Computer build() {
return computer;
}
}
}
这里用了一个内部静态 Builder 类 (简化版建造者),它可以直接访问Computer的私有属性,同时支持链式调用(每个 set 方法返回Builder本身)。
2. 定义具体建造者
根据需求,我们创建两种具体建造者:游戏本建造者和办公本建造者:
java
// 具体建造者1:游戏本建造者
public class GameComputerBuilder {
private Computer.Builder builder;
public GameComputerBuilder() {
builder = new Computer.Builder();
}
// 构建游戏本的方法(预设高性能配件)
public void buildGameComputer() {
builder.setCpu("i9-13900K")
.setMemory("32GB DDR5")
.setHardDisk("1TB SSD")
.setGpu("RTX 4090")
.setOs("Windows 11");
}
// 返回构建好的电脑
public Computer getResult() {
return builder.build();
}
}
// 具体建造者2:办公本建造者
public class OfficeComputerBuilder {
private Computer.Builder builder;
public OfficeComputerBuilder() {
builder = new Computer.Builder();
}
// 构建办公本的方法(预设稳定低耗配件)
public void buildOfficeComputer() {
builder.setCpu("i5-13400")
.setMemory("16GB DDR4")
.setHardDisk("512GB SSD")
.setGpu("集成显卡") // 办公本无需独立显卡
.setOs("Windows 10");
}
public Computer getResult() {
return builder.build();
}
}
3. 定义指挥者
指挥者负责控制构建流程,确保步骤正确:
java
// 指挥者:负责控制电脑组装流程
public class ComputerDirector {
// 指挥建造者按照流程构建产品
public Computer construct(GameComputerBuilder builder) {
builder.buildGameComputer(); // 调用游戏本的构建方法
return builder.getResult();
}
public Computer construct(OfficeComputerBuilder builder) {
builder.buildOfficeComputer(); // 调用办公本的构建方法
return builder.getResult();
}
}
4. 测试代码
最后我们用客户端代码测试一下:
java
public class Client {
public static void main(String[] args) {
// 创建指挥者
ComputerDirector director = new ComputerDirector();
// 构建游戏本
GameComputerBuilder gameBuilder = new GameComputerBuilder();
Computer gamePC = director.construct(gameBuilder);
System.out.println("游戏本配置:");
System.out.println("CPU:" + gamePC.getCpu());
System.out.println("显卡:" + gamePC.getGpu());
// 构建办公本
OfficeComputerBuilder officeBuilder = new OfficeComputerBuilder();
Computer officePC = director.construct(officeBuilder);
System.out.println("\n办公本配置:");
System.out.println("CPU:" + officePC.getCpu());
System.out.println("显卡:" + officePC.getGpu());
}
}
输出结果:
游戏本配置:
CPU:i9-13900K
显卡:RTX 4090
办公本配置:
CPU:i5-13400
显卡:集成显卡
五、建造者模式的优缺点
优点:
- 解耦构建与表示:构建过程(步骤)和产品(结果)分离,同一过程可创建不同产品。
- 控制构建过程:指挥者可以严格控制构建步骤,避免无效对象(比如 "没装 CPU 就装内存")。
- 灵活扩展:新增产品只需新增具体建造者,无需修改原有代码(符合开闭原则)。
- 代码清晰:通过链式调用或分步构建,避免了多参数构造函数的混乱。
缺点:
- 类数量增加:每个产品都需要对应具体建造者,若产品复杂且多样,会导致类数量增多。
- 适用场景有限:只适合构建 "组成部分相似" 的复杂对象,若产品差异过大(比如电脑和手机),不适合用建造者模式。
六、适用场景
建造者模式适合以下场景:
- 构建包含多个组成部分的复杂对象(如电脑、简历、文档)。
- 需要控制对象构建流程(如必须按顺序设置属性)。
- 同一构建过程需要生成不同表示(如同一流程组装不同配置的产品)。
常见的实际应用:
- Java 中的
StringBuilder(通过append方法逐步构建字符串)。 - 很多框架的配置类(如 Spring 的
ResourceBuilder)。 - 建造者模式的简化版(内部 Builder 类)广泛用于 POJO 对象的创建(避免多参数构造函数)。
七、建造者模式 vs 抽象工厂模式
昨天我们讲了抽象工厂模式,它和建造者模式都用于创建对象,但核心区别在于:
- 抽象工厂模式:关注 "创建一系列相关产品"(如创建电脑的同时创建配套的键盘、鼠标),不关心产品的组装过程。
- 建造者模式:关注 "一个复杂产品的逐步构建",核心是控制组装步骤,最终产出一个完整对象。
简单说:抽象工厂是 "批量生产相关零件",建造者是 "用零件一步步拼出一个成品"。
总结
建造者模式通过分离 "构建过程" 和 "产品表示",让复杂对象的创建变得清晰、灵活。它特别适合需要精细控制组装步骤、或同一流程需生成不同产品的场景。
如果你的代码中出现了 "参数爆炸" 的构造函数,或需要频繁创建不同组合的复杂对象,不妨试试建造者模式 ------ 它会像一位耐心的工匠,帮你一步步搭建出完美的 "产品"。