本文为设计模式系列第7篇,聚焦创建型模式中的建造者模式,涵盖定义、原理、实际业务场景、优缺点、最佳实践及详细代码示例,适合系统学习与实战应用。
目录
1. 模式概述
建造者模式(Builder Pattern)是一种创建型设计模式。它将复杂对象的构建与表示分离,使同样的构建过程可以创建不同的表示。适用于需要分步骤、可定制、部件复杂的对象创建。
1.1 定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
1.2 目的
- 分离构建过程与表示,支持灵活扩展
- 简化复杂对象的创建,隐藏细节
- 支持多种产品配置和复用
2. 使用场景
建造者模式在实际开发中应用广泛,常见场景包括:
- 复杂对象组装:如汽车、电脑、房屋等多部件组装,步骤复杂且可定制。
- 文档/报表生成:如HTML、PDF、Word等多格式文档的分步生成。
- UI表单/界面构建:如动态表单、复杂界面分步渲染。
- 配置对象创建:如系统配置、网络配置等参数众多且组合灵活的对象。
真实业务背景举例:
- 电商平台订单对象包含商品、收货、支付、优惠等多个可选部件,建造者模式可灵活组装。
- SaaS平台支持多种报表格式导出,建造者模式可统一流程、灵活扩展。
- 游戏角色装备、技能、属性等复杂对象,建造者模式便于分步构建和扩展。
3. 优缺点分析
3.1 优点
- 封装性:隐藏构建细节,客户端无需关心内部实现。
- 灵活性:支持不同产品配置和多种表示。
- 可维护性:构建过程独立,便于扩展和维护。
3.2 缺点
- 类数量增加:每种产品/部件都需建造者类,结构复杂。
- 适用范围有限:只适用于部件复杂、构建过程稳定的对象。
- 构建过程固定:如步骤变化大,需频繁修改建造者接口。
4. 实际应用案例
- 订单对象组装:商品、收货、支付、优惠等分步组装。
- 报表/文档生成:多格式报表、文档的统一生成流程。
- 汽车/电脑组装:多部件、可选配置的复杂对象。
- UI表单构建:动态表单、复杂界面分步渲染。
5. 结构与UML类图
@startuml
package "Builder Pattern" #DDDDDD {
class Computer {
+ setCpu(cpu: String): void
+ setRam(ram: String): void
+ setStorage(storage: String): void
+ setGpu(gpu: String): void
}
interface ComputerBuilder {
+ buildCpu(): void
+ buildRam(): void
+ buildStorage(): void
+ buildGpu(): void
+ getResult(): Computer
}
class GamingComputerBuilder implements ComputerBuilder
class OfficeComputerBuilder implements ComputerBuilder
class ComputerEngineer {
- builder: ComputerBuilder
+ constructComputer(): void
}
ComputerBuilder <|.. GamingComputerBuilder
ComputerBuilder <|.. OfficeComputerBuilder
ComputerEngineer o-- ComputerBuilder : builder
GamingComputerBuilder --> Computer : getResult()
OfficeComputerBuilder --> Computer : getResult()
}
@enduml
6. 代码示例
6.1 基本结构示例
业务背景: 电脑组装业务,支持多种配置,分步构建。
// 产品类:电脑
package com.example.patterns.builder;
import java.util.Objects;
import java.util.StringJoiner;
public class Computer {
private String cpu;
private String ram;
private String storage;
private String gpu;
public void setCpu(String cpu) {
this.cpu = Objects.requireNonNull(cpu, "CPU cannot be null");
}
public void setRam(String ram) {
this.ram = Objects.requireNonNull(ram, "RAM cannot be null");
}
public void setStorage(String storage) {
this.storage = Objects.requireNonNull(storage, "Storage cannot be null");
}
public void setGpu(String gpu) {
this.gpu = Objects.requireNonNull(gpu, "GPU cannot be null");
}
// 改进的toString方法
@Override
public String toString() {
return new StringJoiner(", ", "Computer{", "}")
.add("cpu='" + cpu + "'")
.add("ram='" + ram + "'")
.add("storage='" + storage + "'")
.add("gpu='" + gpu + "'")
.toString();
}
// 添加equals和hashCode方法,便于测试
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Computer computer = (Computer) o;
return Objects.equals(cpu, computer.cpu) &&
Objects.equals(ram, computer.ram) &&
Objects.equals(storage, computer.storage) &&
Objects.equals(gpu, computer.gpu);
}
@Override
public int hashCode() {
return Objects.hash(cpu, ram, storage, gpu);
}
}
// 抽象建造者
public interface ComputerBuilder {
void buildCpu();
void buildRam();
void buildStorage();
void buildGpu();
Computer getResult();
// 添加重置方法,支持builder复用
default void reset() {
// 子类可以重写此方法实现重置逻辑
}
}
// 游戏电脑建造者
public class GamingComputerBuilder implements ComputerBuilder {
private Computer computer = new Computer();
@Override
public void buildCpu() { computer.setCpu("Intel i9-13900K"); }
@Override
public void buildRam() { computer.setRam("32GB DDR5"); }
@Override
public void buildStorage() { computer.setStorage("2TB NVMe SSD"); }
@Override
public void buildGpu() { computer.setGpu("NVIDIA RTX 4090"); }
@Override
public Computer getResult() { return computer; }
@Override
public void reset() {
computer = new Computer();
}
}
// 办公电脑建造者
public class OfficeComputerBuilder implements ComputerBuilder {
private Computer computer = new Computer();
@Override
public void buildCpu() { computer.setCpu("Intel i5-13600K"); }
@Override
public void buildRam() { computer.setRam("16GB DDR4"); }
@Override
public void buildStorage() { computer.setStorage("1TB SATA SSD"); }
@Override
public void buildGpu() { computer.setGpu("Intel UHD Graphics"); }
@Override
public Computer getResult() { return computer; }
@Override
public void reset() {
computer = new Computer();
}
}
// 组装工程师(导演类)
public class ComputerEngineer {
private ComputerBuilder builder;
public ComputerEngineer(ComputerBuilder builder) {
this.builder = Objects.requireNonNull(builder, "Builder cannot be null");
}
public void constructComputer() {
builder.reset(); // 确保从干净状态开始
builder.buildCpu();
builder.buildRam();
builder.buildStorage();
builder.buildGpu();
}
// 添加部分构建方法,支持定制化
public void constructBasicComputer() {
builder.reset();
builder.buildCpu();
builder.buildRam();
builder.buildStorage();
// 不添加独显
}
}
// 客户端示例
public class Client {
public static void main(String[] args) {
try {
// 创建游戏电脑
ComputerBuilder gamingBuilder = new GamingComputerBuilder();
ComputerEngineer gamingEngineer = new ComputerEngineer(gamingBuilder);
gamingEngineer.constructComputer();
Computer gamingComputer = gamingBuilder.getResult();
System.out.println("Gaming Computer: " + gamingComputer);
// 创建办公电脑
ComputerBuilder officeBuilder = new OfficeComputerBuilder();
ComputerEngineer officeEngineer = new ComputerEngineer(officeBuilder);
officeEngineer.constructComputer();
Computer officeComputer = officeBuilder.getResult();
System.out.println("Office Computer: " + officeComputer);
} catch (Exception e) {
System.err.println("Failed to build computer: " + e.getMessage());
}
}
// 总结:通过建造者模式,客户端可灵活组装不同配置电脑,便于扩展和维护。
}
6.2 实际业务场景:订单对象建造者
业务背景: 电商平台订单对象包含商品、收货、支付、优惠等多个可选部件,建造者模式可灵活组装。
// 订单对象
package com.example.patterns.builder.order;
import java.math.BigDecimal;
import java.util.Objects;
import java.util.StringJoiner;
public class Order {
private String product;
private String address;
private String payment;
private String discount;
private BigDecimal amount; // 添加金额字段
public void setProduct(String product) { this.product = product; }
public void setAddress(String address) { this.address = address; }
public void setPayment(String payment) { this.payment = payment; }
public void setDiscount(String discount) { this.discount = discount; }
public void setAmount(BigDecimal amount) { this.amount = amount; }
public String getProduct() { return product; }
public String getAddress() { return address; }
public String getPayment() { return payment; }
public String getDiscount() { return discount; }
public BigDecimal getAmount() { return amount; }
// 校验订单是否有效
public boolean isValid() {
return product != null && !product.trim().isEmpty() &&
address != null && !address.trim().isEmpty() &&
payment != null && !payment.trim().isEmpty() &&
amount != null && amount.compareTo(BigDecimal.ZERO) > 0;
}
@Override
public String toString() {
return new StringJoiner(", ", "Order{", "}")
.add("product='" + product + "'")
.add("address='" + address + "'")
.add("payment='" + payment + "'")
.add("discount='" + discount + "'")
.add("amount=" + amount)
.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Order order = (Order) o;
return Objects.equals(product, order.product) &&
Objects.equals(address, order.address) &&
Objects.equals(payment, order.payment) &&
Objects.equals(discount, order.discount) &&
Objects.equals(amount, order.amount);
}
@Override
public int hashCode() {
return Objects.hash(product, address, payment, discount, amount);
}
}
// 订单建造者
public class OrderBuilder {
private Order order = new Order();
public OrderBuilder product(String product) {
if (product == null || product.trim().isEmpty()) {
throw new IllegalArgumentException("Product cannot be null or empty");
}
order.setProduct(product);
return this;
}
public OrderBuilder address(String address) {
if (address == null || address.trim().isEmpty()) {
throw new IllegalArgumentException("Address cannot be null or empty");
}
order.setAddress(address);
return this;
}
public OrderBuilder payment(String payment) {
if (payment == null || payment.trim().isEmpty()) {
throw new IllegalArgumentException("Payment method cannot be null or empty");
}
order.setPayment(payment);
return this;
}
public OrderBuilder discount(String discount) {
// 折扣是可选的,允许null
order.setDiscount(discount);
return this;
}
public OrderBuilder amount(BigDecimal amount) {
if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Amount must be positive");
}
order.setAmount(amount);
return this;
}
// 便捷方法,接受double类型
public OrderBuilder amount(double amount) {
return amount(BigDecimal.valueOf(amount));
}
public Order build() {
if (!order.isValid()) {
throw new IllegalStateException("Order is not valid. Missing required fields.");
}
return order;
}
// 重置建造者,支持复用
public OrderBuilder reset() {
order = new Order();
return this;
}
}
// 客户端示例
public class OrderClient {
public static void main(String[] args) {
try {
// 创建完整订单
Order order = new OrderBuilder()
.product("iPhone 15 Pro")
.address("上海市浦东新区")
.payment("微信支付")
.discount("满1000减100")
.amount(8999.00)
.build();
System.out.println("订单信息: " + order);
// 创建无折扣订单
Order simpleOrder = new OrderBuilder()
.product("MacBook Pro")
.address("北京市朝阳区")
.payment("支付宝")
.amount(15999.00)
.build();
System.out.println("简单订单: " + simpleOrder);
} catch (IllegalArgumentException | IllegalStateException e) {
System.err.println("订单创建失败: " + e.getMessage());
}
}
// 总结:通过链式建造者和参数校验,订单对象组装更安全,代码简洁易维护。
}
7. 测试用例
业务背景: 验证电脑建造者和订单建造者的核心功能与多实现切换。
public class BuilderPatternTest {
@Test
public void testGamingComputer() {
ComputerBuilder builder = new GamingComputerBuilder();
ComputerEngineer engineer = new ComputerEngineer(builder);
// 构建游戏电脑
engineer.constructComputer();
Computer computer = builder.getResult();
// 验证游戏电脑配置
String computerStr = computer.toString();
assertTrue(computerStr.contains("Intel i9-13900K"));
assertTrue(computerStr.contains("32GB DDR5"));
assertTrue(computerStr.contains("NVIDIA RTX 4090"));
}
@Test
public void testOfficeComputer() {
ComputerBuilder builder = new OfficeComputerBuilder();
ComputerEngineer engineer = new ComputerEngineer(builder);
// 构建办公电脑
engineer.constructComputer();
Computer computer = builder.getResult();
// 验证办公电脑配置
String computerStr = computer.toString();
assertTrue(computerStr.contains("Intel i5-13600K"));
assertTrue(computerStr.contains("16GB DDR4"));
assertTrue(computerStr.contains("Intel UHD Graphics"));
}
@Test
public void testOrderBuilder() {
// 测试链式建造者
Order order = new OrderBuilder()
.product("iPhone 15 Pro")
.address("上海市浦东新区")
.payment("微信支付")
.discount("满1000减100")
.amount(8999.00)
.build();
assertEquals("iPhone 15 Pro", order.getProduct());
assertEquals("微信支付", order.getPayment());
assertTrue(order.isValid());
}
@Test
public void testDifferentBuilders() {
// 测试不同建造者产生不同结果
ComputerBuilder gamingBuilder = new GamingComputerBuilder();
ComputerBuilder officeBuilder = new OfficeComputerBuilder();
ComputerEngineer engineer1 = new ComputerEngineer(gamingBuilder);
ComputerEngineer engineer2 = new ComputerEngineer(officeBuilder);
engineer1.constructComputer();
engineer2.constructComputer();
Computer gamingComputer = gamingBuilder.getResult();
Computer officeComputer = officeBuilder.getResult();
// 验证两台电脑配置不同
assertNotEquals(gamingComputer.toString(), officeComputer.toString());
}
}
8. 常见误区与反例
- 误区1 :导演类(Director)过度复杂。
如构建逻辑过多应拆分,避免导演类膨胀。 - 误区2 :建造者接口设计不清晰。
应保证步骤明确、职责单一。 - 反例 :客户端直接操作产品部件。
应通过建造者接口组装,避免高耦合。
9. 最佳实践
- 接口设计 :建造者接口要简洁,步骤明确,便于扩展和维护。
- 工厂与建造者结合 :复杂对象可结合工厂方法和建造者模式,提升灵活性。
- 链式调用 :对于参数较多的对象,推荐链式建造者,提升可读性。
- 异常与校验 :建造过程应妥善处理参数校验和异常。
- 文档和UML同步 :保持文档、UML和代码示例一致,便于团队协作。
10. 参考资料与延伸阅读
- 《设计模式:可复用面向对象软件的基础》GoF
- Effective Java(中文版)
- https://refactoringguru.cn/design-patterns/builder
- https://www.baeldung.com/java-builder-pattern
本文为设计模式系列第7篇,后续每篇将聚焦一个设计模式或设计原则,深入讲解实现与应用,敬请关注。