建造者模式深度解析:构建与表示的分离
本文档帮助理解建造者模式的核心概念,以及与模板方法模式、策略模式的区别与联系。
一、建造者模式核心概念
1.1 什么是"构建"与"表示"
建造者模式的经典定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
构建(Construction)
构建 = 组装对象的过程
- 关注点:怎么做(How)
- 内容:一步步创建对象的各个部分
- 特点:构建步骤/流程通常是固定的
表示(Representation)
表示 = 最终生成的对象的具体形态/结构/配置
- 关注点:做什么(What)
- 内容:最终对象的属性组合、结构形态
- 特点:同样的构建过程可以产生完全不同的表示
1.2 核心理解
相同的构建过程,可以产生不同的表示。
这就是"分离"的核心含义:构建算法(步骤)是固定的,但最终生成的对象形态可以不同。
1.3 经典示例:房屋建造
markdown
构建过程(固定步骤):
1. 打地基
2. 建墙体
3. 安装门窗
4. 装修
不同的表示(最终产物):
├── 表示 A:木质别墅
│ ├── 木地基 + 木墙 + 木门窗 + 豪华装修
├── 表示 B:砖混公寓
│ ├── 水泥地基 + 砖墙 + 铝合金门窗 + 简装
└── 表示 C:钢结构厂房
└── 钢地基 + 彩钢板墙 + 卷帘门 + 无装修
构建过程完全一样(都是 4 步),但最终"表示"完全不同。
1.4 代码示例
java
// 产品类(表示)
class House {
private String foundation; // 地基
private String wall; // 墙体
private String door; // 门窗
private String decoration; // 装修
// 不同的表示 = 不同的属性组合
public String getDescription() {
return foundation + " + " + wall + " + " + door + " + " + decoration;
}
}
// 抽象建造者
interface HouseBuilder {
void buildFoundation();
void buildWall();
void buildDoor();
void buildDecoration();
House getHouse();
}
// 具体建造者 1:木质别墅
class WoodenHouseBuilder implements HouseBuilder {
private House house = new House();
public void buildFoundation() { house.setFoundation("木地基"); }
public void buildWall() { house.setWall("木墙"); }
public void buildDoor() { house.setDoor("木门窗"); }
public void buildDecoration() { house.setDecoration("豪华装修"); }
public House getHouse() { return house; }
}
// 具体建造者 2:砖混公寓
class BrickHouseBuilder implements HouseBuilder {
private House house = new House();
public void buildFoundation() { house.setFoundation("水泥地基"); }
public void buildWall() { house.setWall("砖墙"); }
public void buildDoor() { house.setDoor("铝合金门窗"); }
public void buildDecoration() { house.setDecoration("简装"); }
public House getHouse() { return house; }
}
// 指挥者(控制构建过程)
class Director {
private HouseBuilder builder;
public void setBuilder(HouseBuilder builder) {
this.builder = builder;
}
public House construct() {
builder.buildFoundation(); // 步骤 1
builder.buildWall(); // 步骤 2
builder.buildDoor(); // 步骤 3
builder.buildDecoration(); // 步骤 4
return builder.getHouse(); // 返回最终表示
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
Director director = new Director();
// 构建木质别墅
director.setBuilder(new WoodenHouseBuilder());
House woodenHouse = director.construct();
System.out.println(woodenHouse.getDescription());
// 输出:木地基 + 木墙 + 木门窗 + 豪华装修
// 构建砖混公寓
director.setBuilder(new BrickHouseBuilder());
House brickHouse = director.construct();
System.out.println(brickHouse.getDescription());
// 输出:水泥地基 + 砖墙 + 铝合金门窗 + 简装
}
}
1.5 构建 vs 表示 对比表
| 维度 | 构建(Construction) | 表示(Representation) |
|---|---|---|
| 含义 | 组装对象的步骤和过程 | 最终生成的对象形态 |
| 关注点 | 怎么做(How) | 做什么(What) |
| 变化性 | 构建步骤通常固定 | 表示可以多样化 |
| 角色 | Director + Builder 接口 | 具体 Builder 实现类 |
| 示例 | 打地基→建墙→装门→装修 | 木质别墅 / 砖混公寓 / 钢结构厂房 |
1.6 另一个例子:SQL 查询构建器
java
// 构建过程(固定步骤)
class SQLQueryBuilder {
private StringBuilder sql = new StringBuilder();
public SQLQueryBuilder select(String columns) {
sql.append("SELECT ").append(columns);
return this;
}
public SQLQueryBuilder from(String table) {
sql.append(" FROM ").append(table);
return this;
}
public SQLQueryBuilder where(String condition) {
sql.append(" WHERE ").append(condition);
return this;
}
public SQLQueryBuilder orderBy(String column) {
sql.append(" ORDER BY ").append(column);
return this;
}
public String build() {
return sql.toString();
}
}
// 不同的表示(最终 SQL 语句)
public class Client {
public static void main(String[] args) {
SQLQueryBuilder builder = new SQLQueryBuilder();
// 表示 1
String sql1 = builder.select("id, name")
.from("users")
.where("age > 18")
.orderBy("name")
.build();
// SELECT id, name FROM users WHERE age > 18 ORDER BY name
// 表示 2
String sql2 = builder.select("*")
.from("orders")
.where("status = 'paid'")
.build();
// SELECT * FROM orders WHERE status = 'paid'
}
}
构建过程相同 (都是 select→from→where→orderBy→build),但表示不同(生成的 SQL 语句不同)。
二、建造者模式 vs 模板方法模式
2.1 相似点
| 维度 | 相似之处 |
|---|---|
| 步骤固定 | 都有固定的执行步骤/流程 |
| 子类实现细节 | 都由子类实现具体步骤 |
| 流程控制 | 都由父类/指挥者控制流程 |
2.2 核心差异
arduino
模板方法模式:
├── 关注点:算法流程的骨架
├── 变化点:某些步骤的具体实现
├── 输出:同一个算法的不同变体
└── 示例:冲泡饮料(烧水→冲泡→倒入杯子→加调料)
咖啡和茶的区别在于"冲泡"和"加调料"步骤
建造者模式:
├── 关注点:复杂对象的构建过程
├── 变化点:每一步构建的具体内容
├── 输出:完全不同的对象表示
└── 示例:建造房屋(打地基→建墙→装门→装修)
木质别墅和砖混公寓的每一步都不同
2.3 代码对比
java
// ==================== 模板方法模式 ====================
abstract class Beverage {
// 固定流程(模板方法)
final void prepareRecipe() {
boilWater(); // 固定步骤
brew(); // 抽象步骤,子类实现
pourInCup(); // 固定步骤
addCondiments(); // 抽象步骤,子类实现
}
void boilWater() { System.out.println("烧水"); }
void pourInCup() { System.out.println("倒入杯子"); }
abstract void brew();
abstract void addCondiments();
}
class Coffee extends Beverage {
void brew() { System.out.println("冲泡咖啡"); }
void addCondiments() { System.out.println("加糖和牛奶"); }
}
class Tea extends Beverage {
void brew() { System.out.println("浸泡茶叶"); }
void addCondiments() { System.out.println("加柠檬"); }
}
// 使用
Beverage coffee = new Coffee();
coffee.prepareRecipe();
// 输出:烧水 → 冲泡咖啡 → 倒入杯子 → 加糖和牛奶
Beverage tea = new Tea();
tea.prepareRecipe();
// 输出:烧水 → 浸泡茶叶 → 倒入杯子 → 加柠檬
// 注意:输出都是 Beverage 类型,只是某些步骤不同
// ==================== 建造者模式 ====================
interface HouseBuilder {
void buildFoundation(); // 每一步都不同
void buildWall();
void buildDoor();
void buildDecoration();
House getHouse(); // 返回完全不同的对象
}
class WoodenHouseBuilder implements HouseBuilder {
private House house = new House();
void buildFoundation() { house.setFoundation("木地基"); }
void buildWall() { house.setWall("木墙"); }
void buildDoor() { house.setDoor("木门窗"); }
void buildDecoration() { house.setDecoration("豪华装修"); }
House getHouse() { return house; }
}
class BrickHouseBuilder implements HouseBuilder {
private House house = new House();
void buildFoundation() { house.setFoundation("水泥地基"); }
void buildWall() { house.setWall("砖墙"); }
void buildDoor() { house.setDoor("铝合金门窗"); }
void buildDecoration() { house.setDecoration("简装"); }
House getHouse() { return house; }
}
// 使用
Director director = new Director();
director.setBuilder(new WoodenHouseBuilder());
House woodenHouse = director.construct();
// 输出:木地基 + 木墙 + 木门窗 + 豪华装修
director.setBuilder(new BrickHouseBuilder());
House brickHouse = director.construct();
// 输出:水泥地基 + 砖墙 + 铝合金门窗 + 简装
// 注意:输出是完全不同的 House 对象
2.4 关键区别总结
| 对比维度 | 模板方法模式 | 建造者模式 |
|---|---|---|
| 目的 | 定义算法骨架,子类实现部分步骤 | 分离构建过程与表示,创建复杂对象 |
| 变化范围 | 部分步骤不同 | 所有步骤都不同 |
| 输出结果 | 同一类型的不同变体 | 完全不同的对象表示 |
| 流程控制 | 抽象类中的模板方法 | 独立的 Director 类 |
| 继承关系 | 子类继承抽象类 | 实现者实现接口 |
| 典型场景 | 框架设计、算法骨架 | 文档生成、UI 构建、SQL 构建 |
三、建造者模式 vs 策略模式
3.1 相似点
| 维度 | 相似之处 |
|---|---|
| 选择机制 | 都可以根据需要选择不同的实现 |
| 接口抽象 | 都通过接口/抽象类定义契约 |
| 运行时切换 | 都可以在运行时切换实现 |
3.2 核心差异
markdown
策略模式:
├── 关注点:算法的可互换性
├── 变化点:同一个操作的不同算法
├── 输出:相同的结果类型,不同的计算方式
└── 示例:排序策略(快速排序、归并排序、冒泡排序)
输入相同,输出相同,只是算法不同
建造者模式:
├── 关注点:对象构建的可扩展性
── 变化点:构建过程的每一步
├── 输出:完全不同的对象形态
└── 示例:房屋建造者(木质、砖混、钢结构)
输入相同(构建步骤),输出完全不同
3.3 代码对比
java
// ==================== 策略模式 ====================
interface SortStrategy {
void sort(int[] array); // 同一个操作,不同算法
}
class QuickSort implements SortStrategy {
public void sort(int[] array) {
System.out.println("使用快速排序");
// 快速排序实现...
}
}
class MergeSort implements SortStrategy {
public void sort(int[] array) {
System.out.println("使用归并排序");
// 归并排序实现...
}
}
class SortContext {
private SortStrategy strategy;
public void setStrategy(SortStrategy strategy) {
this.strategy = strategy;
}
public void sort(int[] array) {
strategy.sort(array);
}
}
// 使用
SortContext context = new SortContext();
int[] data = {3, 1, 4, 1, 5};
context.setStrategy(new QuickSort());
context.sort(data);
// 输出:使用快速排序 → [1, 1, 3, 4, 5]
context.setStrategy(new MergeSort());
context.sort(data);
// 输出:使用归并排序 → [1, 1, 3, 4, 5]
// 注意:输入相同,输出相同,只是算法不同
// ==================== 建造者模式 ====================
interface HouseBuilder {
void buildFoundation(); // 构建过程的每一步
void buildWall();
void buildDoor();
void buildDecoration();
House getHouse(); // 返回不同的对象
}
class WoodenHouseBuilder implements HouseBuilder {
private House house = new House();
void buildFoundation() { house.setFoundation("木地基"); }
void buildWall() { house.setWall("木墙"); }
void buildDoor() { house.setDoor("木门窗"); }
void buildDecoration() { house.setDecoration("豪华装修"); }
House getHouse() { return house; }
}
class BrickHouseBuilder implements HouseBuilder {
private House house = new House();
void buildFoundation() { house.setFoundation("水泥地基"); }
void buildWall() { house.setWall("砖墙"); }
void buildDoor() { house.setDoor("铝合金门窗"); }
void buildDecoration() { house.setDecoration("简装"); }
House getHouse() { return house; }
}
// 使用
Director director = new Director();
director.setBuilder(new WoodenHouseBuilder());
House woodenHouse = director.construct();
// 输出:木地基 + 木墙 + 木门窗 + 豪华装修
director.setBuilder(new BrickHouseBuilder());
House brickHouse = director.construct();
// 输出:水泥地基 + 砖墙 + 铝合金门窗 + 简装
// 注意:输入相同(构建步骤),输出完全不同的 House 对象
3.4 关键区别总结
| 对比维度 | 策略模式 | 建造者模式 |
|---|---|---|
| 目的 | 封装可互换的算法 | 分离复杂对象的构建与表示 |
| 变化点 | 同一个操作的不同实现 | 构建过程的每一步 |
| 输出结果 | 相同类型,相同结果 | 不同类型的对象表示 |
| 组合方式 | 单一方法替换 | 多步骤组合 |
| 典型场景 | 排序、支付、导航 | 文档生成、UI 构建、SQL 构建 |
四、三种模式的本质区别
4.1 核心思想对比
模板方法模式:
├── 核心思想:好莱坞原则(别调用我们,我们调用你)
├── 控制流:父类控制流程,子类填充细节
└── 类比:填空题(框架已定,填部分内容)
策略模式:
├── 核心思想:算法族的可互换性
├── 控制流:客户端选择策略,上下文执行
└── 类比:选择题(同一问题,不同解法)
建造者模式:
├── 核心思想:构建过程与表示分离
├── 控制流:指挥者控制步骤,建造者实现细节
└── 类比:组装模型(步骤固定,零件不同,成品不同)
4.2 一句话区分
| 模式 | 一句话理解 |
|---|---|
| 模板方法模式 | 流程固定,部分步骤可变 → 同一类产品的不同口味 |
| 策略模式 | 目标相同,算法可变 → 同一问题的不同解法 |
| 建造者模式 | 步骤固定,内容可变 → 相同流程的不同产物 |
4.3 类比理解
烹饪类比:
模板方法模式:
├── 菜谱:炒青菜(洗菜→切菜→热油→炒菜→装盘)
── 变化:炒菠菜 vs 炒生菜(切法和火候不同)
└── 结果:都是炒青菜,只是口味略有不同
策略模式:
├── 目标:把菜做熟
├── 变化:炒 vs 煮 vs 蒸(烹饪方法不同)
└── 结果:菜都熟了,只是做法不同
建造者模式:
├── 流程:准备食材→烹饪→调味→装盘
├── 变化:中餐 vs 西餐 vs 日餐(每步都不同)
└── 结果:完全不同的菜品
五、常见理解误区
5.1 误区一:建造者模式类似模板方法模式
错误理解:建造者的构建过程类似模板方法模式,构建过程中根据对象的不同,最终输出的对象不同。
修正说明:
| 维度 | 模板方法模式 | 建造者模式 |
|---|---|---|
| 变化范围 | 部分步骤不同 | 所有步骤都不同 |
| 输出结果 | 同一类型的不同变体 | 完全不同的对象表示 |
| 流程控制 | 抽象类中的模板方法 | 独立的 Director 类 |
关键区别:模板方法模式中,通常只有部分步骤是抽象的,其他步骤是固定的;而建造者模式中,每一步都可以完全不同。
5.2 误区二:对象不同类似于策略模式
错误理解:对象不同类似于策略模式,根据不同的需要选择不同的对象。
修正说明:
| 维度 | 策略模式 | 建造者模式 |
|---|---|---|
| 输出结果 | 相同类型,相同结果 | 不同类型的对象表示 |
| 组合方式 | 单一方法替换 | 多步骤组合 |
| 目的 | 算法可互换 | 构建与表示分离 |
关键区别:策略模式是同一操作的不同算法,输出结果相同;建造者模式是构建过程相同,输出完全不同的对象。
5.3 更准确的理解
建造者模式 = 固定流程 + 可变实现 + 不同产物
├── 固定流程:Director 控制的构建步骤顺序
├── 可变实现:每个 Builder 实现每一步的具体内容
└── 不同产物:不同 Builder 产生不同形态的对象
类比:
├── 模板方法:同一份菜谱,不同厨师微调某些步骤 → 同一道菜的不同口味
├── 策略模式:同一道数学题,不同解法 → 同一个答案
── 建造者模式:同一套组装步骤,不同零件 → 完全不同的产品
六、实际开发中的选择建议
6.1 模式选择决策树
markdown
需要创建复杂对象?
├── 是 → 对象的构建步骤是否固定?
│ ├── 是 → 每一步的内容是否都不同?
│ │ ├── 是 → 使用建造者模式
│ │ └── 否 → 考虑模板方法模式
│ └── 否 → 考虑工厂模式或抽象工厂模式
└── 否 → 需要 interchangeable 的算法?
├── 是 → 使用策略模式
└── 否 → 考虑其他模式
6.2 场景对照表
| 场景 | 推荐模式 | 原因 |
|---|---|---|
| 算法流程固定,部分步骤需要变化 | 模板方法模式 | 定义骨架,子类填充细节 |
| 同一操作有多种实现,需要动态切换 | 策略模式 | 算法可互换 |
| 复杂对象构建,构建步骤固定但内容不同 | 建造者模式 | 分离构建与表示 |
| 需要链式调用创建对象 | 建造者模式(变种) | 流式 API |
| 创建对象家族 | 抽象工厂模式 | 产品族概念 |
| 简单对象创建 | 工厂方法模式 | 单一产品 |
6.3 实际案例
java
// 场景 1:文档生成(建造者模式)
// 构建步骤固定:标题→正文→页脚
// 但 PDF、HTML、Word 的每一步实现都不同
// → 使用建造者模式
// 场景 2:数据校验(策略模式)
// 目标:验证数据是否合法
// 不同规则:邮箱校验、手机号校验、身份证校验
// → 使用策略模式
// 场景 3:报表生成(模板方法模式)
// 流程固定:查询数据→格式化→生成报表→发送邮件
// 但不同报表的查询和格式化步骤不同
// → 使用模板方法模式
七、总结
7.1 核心要点回顾
| 概念 | 要点 |
|---|---|
| 构建 | 组装对象的步骤和过程(怎么做) |
| 表示 | 最终生成的对象形态(做什么) |
| 分离 | 构建过程固定不变,通过更换 Builder 产生不同表示 |
| 精髓 | 客户端只需知道"构建什么",不需关心"怎么构建" |
7.2 三种模式一句话总结
模板方法模式:流程固定,部分步骤可变 → 同一类产品的不同口味
策略模式:目标相同,算法可变 → 同一问题的不同解法
建造者模式:步骤固定,内容可变 → 相同流程的不同产物
7.3 学习建议
- 理解核心概念:先理解"构建"与"表示"的含义
- 对比学习:将建造者模式与模板方法、策略模式对比学习
- 代码实践:手写三种模式的代码,体会差异
- 场景应用:在实际项目中识别适用场景
文档版本:v1.0 最后更新:2026-05-26