建造者模式:设计与实践
一、什么是建造者模式
1. 基本定义
建造者模式(Builder Pattern)是一种创建型设计模式,由《设计模式:可复用面向对象软件的基础》(GOF著作)定义为:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
该模式通过将复杂对象的创建过程拆分为多个可控步骤,允许开发者分步构建对象,并能在构建过程中进行校验和调整,最终生成符合预期的复杂对象。
2. 核心思想
建造者模式的核心在于分离"对象的构建过程"和"对象的表示形式"。通过引入专门的建造者角色负责对象的构建步骤,客户端只需指定所需对象的类型和内容,无需关心具体的构建细节,从而实现同一构建过程创建不同表现形式的对象。
二、建造者模式的特点
1. 分步构建复杂对象
将复杂对象的创建过程分解为一系列有序的构建步骤,每个步骤负责设置对象的一部分属性或组件,降低构建过程的复杂度。
2. 隔离构建与表示
对象的构建逻辑(如何创建)与对象的表示形式(创建成什么)相互隔离,相同的构建步骤可生成不同的对象实例。
3. 精确控制构建过程
客户端可以通过控制构建步骤的执行顺序和参数,精确控制对象的创建过程,满足不同场景的定制化需求。
4. 支持参数校验
在构建过程中或最终构建完成时,可对对象的属性进行集中校验,确保生成的对象符合业务规则,避免无效对象流转。
5. 链式调用优化
通常采用链式调用的方式设计建造者方法,使代码更具可读性,清晰表达对象的构建逻辑。
| 特点 | 说明 |
|---|---|
| 分步构建 | 将复杂对象创建拆分为有序步骤,降低复杂度 |
| 隔离构建与表示 | 构建逻辑与对象表示分离,相同步骤可生成不同实例 |
| 精确控制 | 客户端可控制步骤顺序和参数,满足定制需求 |
| 参数校验 | 支持构建过程中的集中校验,确保对象有效性 |
| 链式调用 | 采用链式API设计,提升代码可读性 |
三、建造者模式的标准代码实现
1. 模式结构
建造者模式包含四个核心角色:
- 产品(Product):被构建的复杂对象,包含多个组件
- 抽象建造者(Builder):定义构建产品的抽象方法和获取产品的方法
- 具体建造者(ConcreteBuilder):实现抽象建造者的方法,完成具体构建步骤
- 指挥者(Director):控制构建过程,按顺序调用建造者的方法
2. 代码实现示例
2.1 产品类(Product)
/**
* 复杂产品:文档对象
* 包含标题、内容段落、图片、签名等组件
*/
public class Document {
private String title;
private List<String> paragraphs = new ArrayList<>();
private List<String> images = new ArrayList<>();
private String signature;
private String footer;
// 产品的使用方法
public void display() {
System.out.println("标题:" + title);
System.out.println("内容:");
paragraphs.forEach(p -> System.out.println("- " + p));
images.forEach(img -> System.out.println("图片:" + img));
System.out.println("签名:" + signature);
System.out.println("页脚:" + footer);
}
// 为建造者提供属性设置方法(通常为package-private或通过Builder内部类访问)
void setTitle(String title) {
this.title = title;
}
void addParagraph(String paragraph) {
this.paragraphs.add(paragraph);
}
void addImage(String image) {
this.images.add(image);
}
void setSignature(String signature) {
this.signature = signature;
}
void setFooter(String footer) {
this.footer = footer;
}
}
2.2 抽象建造者(Builder)
/**
* 文档建造者接口
* 定义文档构建的步骤
*/
public interface DocumentBuilder {
void buildTitle(String title);
void buildParagraph(String content);
void buildImage(String imagePath);
void buildSignature(String signature);
void buildFooter(String footer);
Document getResult();
}
2.3 具体建造者(ConcreteBuilder)
/**
* 报告文档建造者
* 实现具体的构建逻辑
*/
public class ReportDocumentBuilder implements DocumentBuilder {
private Document document = new Document();
@Override
public void buildTitle(String title) {
// 报告标题格式处理
document.setTitle("[报告] " + title);
}
@Override
public void buildParagraph(String content) {
// 报告段落格式处理
document.addParagraph("■ " + content);
}
@Override
public void buildImage(String imagePath) {
// 报告图片格式处理
document.addImage("报告附图:" + imagePath);
}
@Override
public void buildSignature(String signature) {
// 报告签名格式处理
document.setSignature("报告人:" + signature);
}
@Override
public void buildFooter(String footer) {
// 报告页脚格式处理
document.setFooter("报告生成日期:" + LocalDate.now() + " | " + footer);
}
@Override
public Document getResult() {
// 构建完成后进行校验
if (document.getTitle() == null || document.getTitle().isEmpty()) {
throw new IllegalStateException("报告标题不能为空");
}
if (document.getSignature() == null || document.getSignature().isEmpty()) {
throw new IllegalStateException("报告必须包含签名");
}
return document;
}
}
2.4 指挥者(Director)
/**
* 文档构建指挥者
* 控制文档的构建流程
*/
public class DocumentDirector {
private DocumentBuilder builder;
public DocumentDirector(DocumentBuilder builder) {
this.builder = builder;
}
// 构建简单报告(标题+段落+签名)
public Document buildSimpleReport(String title, List<String> contents, String signature) {
builder.buildTitle(title);
contents.forEach(content -> builder.buildParagraph(content));
builder.buildSignature(signature);
return builder.getResult();
}
// 构建完整报告(标题+段落+图片+签名+页脚)
public Document buildFullReport(String title, List<String> contents, List<String> images,
String signature, String footer) {
builder.buildTitle(title);
contents.forEach(content -> builder.buildParagraph(content));
images.forEach(image -> builder.buildImage(image));
builder.buildSignature(signature);
builder.buildFooter(footer);
return builder.getResult();
}
}
2.5 客户端使用
public class Client {
public static void main(String[] args) {
// 创建具体建造者
DocumentBuilder builder = new ReportDocumentBuilder();
// 创建指挥者
DocumentDirector director = new DocumentDirector(builder);
// 构建简单报告
List<String> contents = Arrays.asList(
"项目进度:已完成60%",
"主要成果:完成核心模块开发",
"下一步计划:进行系统测试"
);
Document simpleReport = director.buildSimpleReport("项目进展报告", contents, "张三");
simpleReport.display();
// 构建完整报告
List<String> images = Arrays.asList("/images/progress.png", "/images/chart.png");
Document fullReport = director.buildFullReport("项目验收报告", contents, images, "李四", "保密等级:内部");
fullReport.display();
}
}
3. 代码实现特点总结
| 角色 | 核心职责 | 代码特点 |
|---|---|---|
| 产品(Document) | 定义复杂对象的组成 | 包含多个组件属性,提供使用方法 |
| 抽象建造者(DocumentBuilder) | 定义构建步骤接口 | 声明所有可能的构建方法,返回产品的方法 |
| 具体建造者(ReportDocumentBuilder) | 实现构建步骤,完成实际构建 | 持有产品实例,实现每个步骤的具体逻辑,提供校验 |
| 指挥者(DocumentDirector) | 控制构建流程 | 封装构建算法,按固定顺序调用建造者方法 |
四、支付框架设计中建造者模式的运用
以支付对账数据构建器为例,说明建造者模式在支付系统中的具体实现:
1. 场景分析
支付系统每日需与银行、第三方支付渠道进行对账,对账数据包含订单基本信息、交易流水、金额明细、状态标识、加密签名等多维度数据。不同渠道的对账格式(CSV/XML)、加密方式(AES/RSA)、字段规则存在差异,且需支持灵活扩展新渠道。
2. 设计实现
2.1 对账数据产品类
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
* 支付对账数据(产品类)
* 包含对账所需的核心信息
*/
public class ReconciliationData {
// 基础信息
private String orderId; // 订单号
private String channelTxId; // 渠道交易号
private String merchantId; // 商户号
private BigDecimal amount; // 交易金额
private String currency; // 币种
private String transactionType; // 交易类型(支付/退款)
private String status; // 状态(成功/失败)
// 时间信息
private LocalDateTime orderTime; // 下单时间
private LocalDateTime payTime; // 支付时间
private LocalDateTime reconciliationTime; // 对账时间
// 安全信息
private String encryptedData; // 加密数据
private String signature; // 签名
private String signType; // 签名类型
// 扩展信息(渠道特有字段)
private Map<String, String> extraFields = new HashMap<>();
// 私有构造函数,仅允许建造者访问
private ReconciliationData() {}
// Getter方法(无Setter,确保对象创建后不可变)
public String getOrderId() { return orderId; }
public String getChannelTxId() { return channelTxId; }
public String getMerchantId() { return merchantId; }
public BigDecimal getAmount() { return amount; }
public String getCurrency() { return currency; }
public String getTransactionType() { return transactionType; }
public String getStatus() { return status; }
public LocalDateTime getOrderTime() { return orderTime; }
public LocalDateTime getPayTime() { return payTime; }
public LocalDateTime getReconciliationTime() { return reconciliationTime; }
public String getEncryptedData() { return encryptedData; }
public String getSignature() { return signature; }
public String getSignType() { return signType; }
public Map<String, String> getExtraFields() { return new HashMap<>(extraFields); }
// 建造者内部类
public static class Builder {
private final ReconciliationData data = new ReconciliationData();
// 基础信息设置(必填项)
public Builder orderId(String orderId) {
data.orderId = orderId;
return this;
}
public Builder channelTxId(String channelTxId) {
data.channelTxId = channelTxId;
return this;
}
public Builder merchantId(String merchantId) {
data.merchantId = merchantId;
return this;
}
public Builder amount(BigDecimal amount) {
data.amount = amount;
return this;
}
// 可选信息设置
public Builder currency(String currency) {
data.currency = currency;
return this;
}
public Builder transactionType(String transactionType) {
data.transactionType = transactionType;
return this;
}
public Builder status(String status) {
data.status = status;
return this;
}
public Builder orderTime(LocalDateTime orderTime) {
data.orderTime = orderTime;
return this;
}
public Builder payTime(LocalDateTime payTime) {
data.payTime = payTime;
return this;
}
public Builder reconciliationTime(LocalDateTime reconciliationTime) {
data.reconciliationTime = reconciliationTime;
return this;
}
// 安全相关设置
public Builder encryptedData(String encryptedData) {
data.encryptedData = encryptedData;
return this;
}
public Builder signature(String signature, String signType) {
data.signature = signature;
data.signType = signType;
return this;
}
// 扩展字段设置
public Builder addExtraField(String key, String value) {
data.extraFields.put(key, value);
return this;
}
/**
* 构建对账数据并校验
*/
public ReconciliationData build() {
// 基础校验
if (data.orderId == null || data.orderId.isEmpty()) {
throw new IllegalArgumentException("订单号不能为空");
}
if (data.channelTxId == null || data.channelTxId.isEmpty()) {
throw new IllegalArgumentException("渠道交易号不能为空");
}
if (data.amount == null || data.amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("交易金额必须大于0");
}
if (data.reconciliationTime == null) {
data.reconciliationTime = LocalDateTime.now(); // 默认当前时间
}
if (data.currency == null) {
data.currency = "CNY"; // 默认人民币
}
// 安全校验(生产环境需严格校验)
if (data.encryptedData == null && data.signature == null) {
throw new SecurityException("对账数据必须包含加密信息或签名");
}
return data;
}
}
}
2.2 渠道对账建造者接口
import java.util.List;
/**
* 对账数据建造者接口
* 定义不同渠道对账数据的构建规范
*/
public interface ReconciliationBuilder {
// 设置基础订单信息
ReconciliationBuilder withOrderInfo(String orderId, String merchantId, BigDecimal amount);
// 设置渠道交易信息
ReconciliationBuilder withChannelInfo(String channelTxId, String transactionType, String status);
// 设置时间信息
ReconciliationBuilder withTimeInfo(LocalDateTime orderTime, LocalDateTime payTime);
// 添加扩展字段
ReconciliationBuilder addExtraFields(List<ExtraField> fields);
// 加密处理
ReconciliationBuilder encrypt(String secretKey);
// 签名处理
ReconciliationBuilder sign(String privateKey);
// 构建对账数据
ReconciliationData build();
}
/**
* 扩展字段实体
*/
class ExtraField {
private String key;
private String value;
public ExtraField(String key, String value) {
this.key = key;
this.value = value;
}
public String getKey() { return key; }
public String getValue() { return value; }
}
2.3 具体渠道建造者(支付宝为例)
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.LocalDateTime;
import java.util.Base64;
import java.util.List;
/**
* 支付宝对账数据建造者
* 实现支付宝渠道的对账数据构建逻辑
*/
public class AlipayReconciliationBuilder implements ReconciliationBuilder {
private final ReconciliationData.Builder dataBuilder = new ReconciliationData.Builder();
@Override
public ReconciliationBuilder withOrderInfo(String orderId, String merchantId, BigDecimal amount) {
dataBuilder.orderId(orderId)
.merchantId(merchantId)
.amount(amount)
.currency("CNY"); // 支付宝默认人民币
return this;
}
@Override
public ReconciliationBuilder withChannelInfo(String channelTxId, String transactionType, String status) {
dataBuilder.channelTxId(channelTxId)
.transactionType(transactionType)
.status(status)
// 支付宝特有扩展字段
.addExtraField("alipay_service", "alipay.trade.pay");
return this;
}
@Override
public ReconciliationBuilder withTimeInfo(LocalDateTime orderTime, LocalDateTime payTime) {
dataBuilder.orderTime(orderTime)
.payTime(payTime);
return this;
}
@Override
public ReconciliationBuilder addExtraFields(List<ExtraField> fields) {
fields.forEach(field -> dataBuilder.addExtraField(field.getKey(), field.getValue()));
return this;
}
@Override
public ReconciliationBuilder encrypt(String secretKey) {
try {
// 支付宝采用AES-128-CBC加密
SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
// 待加密内容(实际场景需序列化关键信息)
String content = "orderId=" + dataBuilder.build().getOrderId() +
"&amount=" + dataBuilder.build().getAmount();
byte[] encrypted = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
dataBuilder.encryptedData(Base64.getEncoder().encodeToString(encrypted));
} catch (Exception e) {
throw new SecurityException("支付宝对账数据加密失败", e);
}
return this;
}
@Override
public ReconciliationBuilder sign(String privateKey) {
try {
// 支付宝采用RSA签名
String content = "orderId=" + dataBuilder.build().getOrderId() +
"&channelTxId=" + dataBuilder.build().getChannelTxId() +
"&amount=" + dataBuilder.build().getAmount();
String signature = RsaUtils.sign(content, privateKey, "UTF-8");
dataBuilder.signature(signature, "RSA");
} catch (Exception e) {
throw new SecurityException("支付宝对账数据签名失败", e);
}
return this;
}
@Override
public ReconciliationData build() {
return dataBuilder.build();
}
}
2.4 对账服务(指挥者)
/**
* 对账服务(指挥者)
* 控制对账数据的构建流程
*/
public class ReconciliationService {
private final ReconciliationBuilder builder;
// 注入具体建造者(依赖注入)
public ReconciliationService(ReconciliationBuilder builder) {
this.builder = builder;
}
/**
* 构建标准对账数据
*/
public ReconciliationData buildStandardData(String orderId, String merchantId, BigDecimal amount,
String channelTxId, String status, LocalDateTime orderTime) {
return builder.withOrderInfo(orderId, merchantId, amount)
.withChannelInfo(channelTxId, "PAY", status)
.withTimeInfo(orderTime, null)
.sign(getMerchantPrivateKey(merchantId)) // 签名
.build();
}
/**
* 构建加密对账数据(高安全级别)
*/
public ReconciliationData buildEncryptedData(String orderId, String merchantId, BigDecimal amount,
String channelTxId, String status, List<ExtraField> extraFields) {
return builder.withOrderInfo(orderId, merchantId, amount)
.withChannelInfo(channelTxId, "REFUND", status)
.withTimeInfo(null, LocalDateTime.now())
.addExtraFields(extraFields)
.encrypt(getChannelSecretKey()) // 加密
.sign(getMerchantPrivateKey(merchantId)) // 签名
.build();
}
// 获取商户私钥(实际从安全存储中获取)
private String getMerchantPrivateKey(String merchantId) {
return merchantKeyRepository.getPrivateKey(merchantId);
}
// 获取渠道密钥(实际从安全存储中获取)
private String getChannelSecretKey() {
return channelConfig.getSecretKey();
}
}
2.5 客户端使用示例
public class ReconciliationClient {
public static void main(String[] args) {
// 支付宝对账场景
ReconciliationBuilder alipayBuilder = new AlipayReconciliationBuilder();
ReconciliationService service = new ReconciliationService(alipayBuilder);
// 构建标准对账数据
ReconciliationData standardData = service.buildStandardData(
"ORD20230001", "MCH001", new BigDecimal("100.00"),
"2023000001", "SUCCESS", LocalDateTime.of(2023, 10, 1, 10, 30)
);
// 构建加密对账数据(退款场景)
List<ExtraField> extraFields = Arrays.asList(
new ExtraField("refund_reason", "商品质量问题"),
new ExtraField("refund_fee", "10.00")
);
ReconciliationData encryptedData = service.buildEncryptedData(
"ORD20230002", "MCH001", new BigDecimal("10.00"),
"2023000002", "SUCCESS", extraFields
);
// 提交对账
reconciliationClient.submit(standardData);
reconciliationClient.submit(encryptedData);
}
}
3. 模式价值体现
- 灵活性:新增支付渠道时,只需实现
ReconciliationBuilder接口,无需修改对账服务核心逻辑 - 可读性:链式调用清晰表达对账数据的构建过程,比传统setter方法更直观
- 安全性:在
build()方法中集中校验数据完整性和安全性,避免无效数据进入对账流程 - 可维护性:将渠道特有逻辑(如加密、签名)封装在具体建造者中,职责单一,便于维护
五、开源框架中建造者模式的运用
以MyBatis框架中的SqlSessionFactoryBuilder为例,说明建造者模式的框架级应用:
1. 核心实现分析
1.1 产品类与建造者
MyBatis中,SqlSessionFactory是核心产品类(用于创建SqlSession),SqlSessionFactoryBuilder是具体建造者,负责构建SqlSessionFactory。
// 产品类:SqlSessionFactory(接口)
public interface SqlSessionFactory {
SqlSession openSession();
// 其他方法...
}
// 具体产品实现
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
// 实现方法...
}
// 建造者类:SqlSessionFactoryBuilder
public class SqlSessionFactoryBuilder {
// 构建SqlSessionFactory(核心建造方法)
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 解析配置文件(分步构建)
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 构建并返回产品
return build(parser.parse());
} catch (Exception e) {
// 异常处理...
}
}
// 核心构建方法
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
1.2 构建流程
- 解析配置文件(XML/注解)
- 构建
Configuration对象(包含数据源、映射器、插件等配置) - 基于
Configuration创建DefaultSqlSessionFactory
1.3 客户端使用
// 客户端代码
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 使用建造者构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 使用产品
SqlSession session = sqlSessionFactory.openSession();
2. 建造者模式在MyBatis中的价值
- 分步构建复杂对象:
SqlSessionFactory依赖Configuration,而Configuration的构建需要解析数据源、映射文件、插件等,建造者模式将这一复杂过程拆分为多个步骤 - 多源输入支持:
SqlSessionFactoryBuilder提供多种build()重载方法,支持从InputStream、Reader、Configuration等不同输入构建产品 - 隔离构建细节:客户端无需关心
Configuration的构建过程,只需通过建造者获取SqlSessionFactory - 灵活性:通过不同参数(如
environment)可构建不同环境的SqlSessionFactory(开发/生产环境)
六、总结
1. 建造者模式的适用场景
- 当对象包含多个组件,且构建过程需要多步骤时
- 当需要创建不同表示的复杂对象(如不同格式的文档、不同渠道的对账数据)
- 当对象创建需要严格的参数校验或安全处理时
- 当希望构建过程具有良好的可读性和可维护性时
2. 与其他模式的区别
- 与工厂模式:工厂模式侧重"创建什么",建造者模式侧重"如何创建";工厂模式适用于简单对象,建造者模式适用于复杂对象的分步构建
- 与抽象工厂模式:抽象工厂模式创建产品族,建造者模式创建单个复杂产品;抽象工厂模式的产品之间相互关联,建造者模式的产品组件更灵活
3. 支付系统中的实践价值
- 复杂对象标准化构建:对账数据、支付请求等复杂对象的构建过程标准化,减少人为错误
- 多渠道适配:不同支付渠道的差异化处理(加密、格式)封装在具体建造者中,便于扩展
- 安全性保障:在构建过程中集成签名、加密等安全校验,确保支付数据的完整性
- 代码可读性提升:链式调用使参数设置逻辑清晰,降低后续维护成本
建造者模式通过分离构建与表示,为支付系统中复杂对象的创建提供了灵活、可控、可读的解决方案,是处理多参数、多步骤、多变化场景的理想选择。