一、现实中的建造者模式
场景1:定制披萨
当你在披萨店点餐时:
- 1. 选择基础款(必须选):9寸/12寸面饼
- 2. 添加配料(可选):芝士、培根、蘑菇、洋葱...
- 3. 选择酱料(可选):番茄酱、蒜香酱
- 4. 最终得到一个完整披萨
这个过程就是建造者模式的现实映射——分步骤构建复杂对象。
二、建造者模式的核心思想
核心目标:
将复杂对象的构造过程与对象表示分离,使得同样的构建过程可以创建不同的对象表示。
四大角色:
- 1. 产品(Product):最终要构建的复杂对象
- 2. 抽象建造者(Builder):定义构建步骤的接口
- 3. 具体建造者(Concrete Builder):实现具体构建逻辑
- 4. 指挥者(Director):控制构建流程(可选)
UML类图:
三、代码实战:构建游戏角色
需求描述:
创建一个游戏角色,包含:
- o 必须属性:姓名、职业
- o 可选属性:武器、盔甲、技能
- o 约束条件:战士不能装备法杖
步骤1:定义产品类
class GameCharacter {
privatefinal String name; // 必选
privatefinal String job; // 必选
private String weapon; // 可选
private String armor; // 可选
private List skills = newArrayList<>(); // 可选
// 私有构造方法,只能通过Builder创建
private GameCharacter(Builder builder) {
this.name = builder.name;
this.job = builder.job;
this.weapon = builder.weapon;
this.armor = builder.armor;
this.skills = builder.skills;
}
// 显示角色信息
public void showInfo() {
System.out.println("姓名:" + name);
System.out.println("职业:" + job);
System.out.println("武器:" + (weapon != null ? weapon : "无"));
System.out.println("盔甲:" + (armor != null ? armor : "无"));
System.out.println("技能:" + skills);
}
// 建造者类
public static class Builder {
private final String name;
private final String job;
private String weapon;
private String armor;
private List skills = newArrayList<>();
// 必选参数通过构造方法传入
public Builder(String name, String job) {
this.name = name;
this.job = job;
}
// 可选参数的设置方法(返回Builder实现链式调用)
public Builder weapon(String weapon) {
// 职业校验
if ("战士".equals(job) && "法杖".equals(weapon)) {
thrownewIllegalArgumentException("战士不能装备法杖!");
}
this.weapon = weapon;
returnthis;
}
public Builder armor(String armor) {
this.armor = armor;
returnthis;
}
public Builder skill(String skill) {
this.skills.add(skill);
returnthis;
}
// 最终构建方法
public GameCharacter build() {
returnnewGameCharacter(this);
}
}
}
步骤2:客户端使用
public class Client {
public static void main(String[] args) {
// 构建战士角色
GameCharacterwarrior=newGameCharacter.Builder("亚瑟", "战士")
.weapon("巨剑")
.armor("板甲")
.skill("旋风斩")
.skill("盾击")
.build();
warrior.showInfo();
// 构建法师角色
GameCharactermage=newGameCharacter.Builder("吉安娜", "法师")
.weapon("法杖")
.skill("火球术")
.skill("寒冰箭")
.build();
mage.showInfo();
}
}
输出结果:
姓名:亚瑟
职业:战士
武器:巨剑
盔甲:板甲
技能:[旋风斩, 盾击]
姓名:吉安娜
职业:法师
武器:法杖
盔甲:无
技能:[火球术, 寒冰箭]
四、建造者模式的核心优势
1. 与传统创建方式的对比
方式1:重叠构造器
// 参数爆炸的构造方法
public GameCharacter(String name, String job) {...}
public GameCharacter(String name, String job, String weapon) {...}
public GameCharacter(String name, String job, String weapon, String armor) {...}
缺点:参数顺序容易出错,难以维护
方式2:JavaBean模式
GameCharacter c = new GameCharacter();
c.setName("亚瑟");
c.setJob("战士");
// ...其他setter
缺点:对象状态在构造过程中不一致
建造者模式优势:
- o 参数灵活:可选参数自由组合
- o 不可变对象:构建完成后对象状态不可变
- o 链式调用:代码可读性强
- o 参数校验:构建时进行业务校验
五、典型应用场景
1. 复杂配置对象
// 创建数据库连接配置
DataSourceConfig config = new DataSourceConfig.Builder()
.url("jdbc:mysql://localhost:3306/mydb")
.username("root")
.password("123456")
.maxPoolSize(20)
.minIdle(5)
.build();
2. 文档生成器
// 构建HTML文档
HtmlDocument doc = new HtmlDocument.Builder()
.header("")
.body("Hello World
")
.footer("Copyright 2023
")
.build();
3. 套餐组合
// 手机套餐定制
MobilePackage package = new MobilePackage.Builder()
.basePlan("畅享套餐")
.addDataPack("10GB流量包")
.addVoicePack("500分钟通话")
.addService("国际漫游")
.build();
六、建造者模式变种
变种1:带指挥者的建造者
class CharacterDirector {
public GameCharacter createWarrior(Builder builder) {
return builder
.weapon("长剑")
.armor("锁甲")
.skill("冲锋")
.build();
}
public GameCharacter createMage(Builder builder) {
return builder
.weapon("法杖")
.skill("火球术")
.build();
}
}
// 使用示例
Builder builder=new GameCharacter.Builder("玩家1", "战士");
CharacterDirector director=new CharacterDirector();
GameCharacter warrior= director.createWarrior(builder);
变种2:通用建造者接口
interface Builder {
T build();
}
classCarBuilderimplementsBuilder {
// 实现具体构建逻辑
}
classHouseBuilderimplementsBuilder {
// 实现具体构建逻辑
}
七、在开源框架中的应用
1. Lombok的@Builder注解
@Builder
publicclassUser {
private String name;
privateint age;
private String email;
}
// 自动生成建造者
User user= User.builder()
.name("张三")
.age(25)
.email("zhangsan@example.com")
.build();
2. OkHttp的Request构建
Request request = new Request.Builder()
.url("https://api.example.com")
.header("Authorization", "Bearer token")
.post(requestBody)
.build();
3. Java Stream API
String result = Stream.builder()
.add("A")
.add("B")
.add("C")
.build()
.collect(Collectors.joining(","));
八、优缺点分析
优点:
- 1. 参数灵活:轻松处理大量可选参数
- 2. 不可变对象:构建完成后线程安全
- 3. 代码可读:链式调用清晰表达意图
- 4. 参数校验:可在build()方法中统一校验
缺点:
- 1. 代码冗余:需要额外编写Builder类
- 2. 性能损耗:创建Builder对象的开销
- 3. 学习成本:对新手不够直观
九、常见问题解答
Q1:建造者模式 vs 工厂模式?
- o 工厂模式:关注产品整体创建,隐藏具体类型
- o 建造者模式:关注分步构建过程,处理复杂参数
Q2:什么时候应该使用建造者模式?
- o 当构造方法参数超过4个
- o 存在大量可选参数时
- o 需要创建不可变对象时
Q3:如何保证构建过程的线程安全?
- o 每个线程使用独立的Builder实例
- o 避免在Builder中保存可变状态
Q4:如何处理继承关系?
- o 使用泛型递归类型
abstract class BaseBuilder<T extendsBaseBuilder> {
protected String commonField;
public T commonMethod(String value) {
this.commonField = value;
return self();
}
protected abstract T self();
}
class ConcreteBuilder extends BaseBuilder {
protected ConcreteBuilder self() { returnthis; }
}
十、总结与最佳实践
核心价值:
- o 将复杂对象的构造代码与业务逻辑代码分离
- o 提供清晰的参数传递规范
- o 支持不可变对象的安全创建
使用建议:
- 1. 当参数超过4个时优先考虑
- 2. 对必选参数使用构造方法传入
- 3. 在build()方法中进行参数校验
- 4. 结合Lombok简化代码编写
扩展思考:
- 1. 如何实现动态参数(如不定数量的技能)?
→ 使用集合类型的构建方法 - 2. 如何实现参数依赖校验?
→ 在build()方法中统一检查 - 3. 如何实现多语言支持?
→ 创建不同语言的Builder实现
掌握建造者模式,就像获得了一把构造复杂对象的瑞士军刀,让代码在应对多变需求时依然保持优雅与灵活。这种分步构建的艺术,将使你的代码库更具工程美感。