妙哉!这个业务语义化的Builder模式,让对象构建变得更稳、更准、更快
还在用传统的builder模式来实例化构建对象吗?来,给你的程序注入点新鲜血液🩸
业务导向的Builder模式在AuditSaveVO中的卓越实践
在复杂的业务系统开发中,我们经常面临如何优雅构建数据对象的挑战。传统的构造器模式在业务语义表达方面存在明显不足。今天我们将深入分析一种基于业务语义导向的Builder模式在AuditSaveVO类中的精妙实现。
传统构建方式的局限性
常规的对象构建方式通常采用以下几种模式:
// 1. 多参数构造器 - 参数顺序易错
new AuditSaveVO(orderNo, bizType, auditorId, auditorName, node, remark, remarkType, statusDesc);
// 2. Setter链式调用 - 业务关联性丢失
new AuditSaveVO()
.setBizOrderNo("ORD001")
.setBizType(BizTypeEnum.AGENT_CHANGE)
...
.setAuditorId(1001L);
// 3. builder模式链式赋值
AuditSaveVO.builder()
.bizOrderNo(r"ORD001")
.bizType(BizTypeEnum.AGENT_CHANGE)
...
.build();
这些方式存在三个核心问题:
- 业务语义缺失:订单号
bizOrderNo与业务类型bizType是紧密关联的业务概念,却被拆分为独立操作 - 构建顺序混乱:缺乏对构建步骤的强制约束,可能导致业务逻辑错误
- 参数校验困难:无法在编译期确保必填参数的完整性,这往往是生产运行期BUG的潜在因素
AuditSaveVO的业务语义化Builder实现
我们的AuditSaveVO采用了全新的构建范式:
/**
* 审核记录持久化VO - 采用业务语义化Builder模式
* 构建对象请使用 AuditSaveVO.builder()
*/
@ToString
@Getter
public class AuditSaveVO implements Serializable {
// 字段定义...
private AuditSaveVO() {} // 私有构造器
/**
* ★★★ 构建器入口方法 ★★★
*/
public static BizOrderStep builder() {
return new AuditSaveVOBuilder();
}
// 构建步骤接口定义
private interface BizOrderStep {
AuditNodeStep withBizOrder(String bizOrderNo, BizTypeEnum bizType);
}
private interface AuditNodeStep {
OperationStep withAuditNode(AuditNodeEnum auditNodeEnum);
}
// 更多步骤接口...
// 构建器实现
private static class AuditSaveVOBuilder implements BizOrderStep, AuditNodeStep, OperationStep, RemarkStep, AuditStatusDescStep, BuildStep {
private final AuditSaveVO auditSaveVO = new AuditSaveVO();
@Override
public AuditNodeStep withBizOrder(String bizOrderNo, BizTypeEnum bizType) {
// 业务单元聚合:订单号+业务类型
auditSaveVO.bizOrderNo = bizOrderNo;
auditSaveVO.bizType = bizType;
return this;
}
// 其他接口实现...
}
}
核心是三步:
- 接口步骤定义(契约先行)
- 构建器实现(业务内聚)--AuditSaveVOBuilder
- 值对象设计- - AuditSaveVO屏蔽setter + 定义builder静态方法
实际应用效果如下:
// 业务代码中的使用示例,对象构造变得so easy!
AuditSaveVO record = AuditSaveVO.builder()
.withBizOrder(application.getId(), BizTypeEnum.APPLICATION_AUDIT)
.withAuditNode(AuditNodeEnum.FIRST_APPROVAL)
.withAuditor(currentUser.getId(), currentUser.getName())
.withRemark("材料齐全,符合要求", "APPROVE")
.withAuditStatusDesc("一审通过")
.build();
设计精髓与业务价值
-
业务单元聚合设计
// 将业务上紧密关联的字段组合在一起设置 .withBizOrder("ORD001", BizTypeEnum.AGENT_CHANGE)这种方式保持了业务概念的完整性,避免了字段分散设置带来的语义割裂。业务意图更加明确,大大提升代码可读性。
-
构建流程强约束
通过接口返回值类型限定,确保了构建步骤必须按照业务逻辑顺序执行:withBizOrder → withAuditNode → withAuditor → withRemark → withAuditStatusDesc → build
有了这个强制约束,开发者再无需care、无需worry是否忘记给个别field赋值,从而提高开发效率。
-
编译期安全保证
由于构建步骤的接口约束,开发者无法跳过必需步骤或颠倒顺序,大大减少了对象构建相关的BUG。 -
非必需字段可不赋值————灵活性的拿捏
对于AuditSaveVO来说,假设auditStatusDesc在对象构造时可以忽略赋值,则我们可以修改前面的RemarkStep这个interface,为其添加build方法,即可跳过withAuditStatusDesc,直接调用最终的build方法。AuditSaveVO record = AuditSaveVO.builder() .withBizOrder(application.getId(), BizTypeEnum.APPLICATION_AUDIT) .withAuditNode(AuditNodeEnum.FIRST_APPROVAL) .withAuditor(currentUser.getId(), currentUser.getName()) .withRemark("材料齐全,符合要求", "APPROVE") //**`RemarkStep`这个interface有`build`方法,可跳过`withAuditStatusDesc`直接调用`build`完成对象的构建 //.withAuditStatusDesc("一审通过") .build();
后记:重构优化 - 将Builder从VO中分离的实现方案
虽然内聚式的Builder设计已经解决了业务语义表达的问题,但从架构角度出发,我们可以进一步优化,将Builder从VO主体中完全分离,实现更清晰的职责划分。
重构动机与目标
分离的目的:
- 单一职责原则:VO应专注于数据承载,Builder专注于对象构建
- 降低耦合度:避免VO类随着构建逻辑的复杂而膨胀
- 增强可测试性:构建逻辑可以独立测试
- 提高复用性:同一构建器可以支持多种VO变体
重构实施方案
步骤一:定义独立的构建步骤接口
// AuditSaveBuildSteps.java - 独立的构建步骤接口定义
/*此class不对package外暴露*/ interface AuditSaveBuildSteps {
interface BizOrderStep {
AuditNodeStep withBizOrder(String bizOrderNo, BizTypeEnum bizType);
}
interface AuditNodeStep {
OperationStep withAuditNode(AuditNodeEnum auditNodeEnum);
}
// 其他步骤接口...
}
步骤二:实现独立的构建器类
// AuditSaveVOBuilder.java - 独立的构建器实现
/*此class不对package外暴露*/ class AuditSaveVOBuilder implements AuditSaveBuildSteps.BizOrderStep,
AuditSaveBuildSteps.AuditNodeStep,
AuditSaveBuildSteps.OperationStep,
AuditSaveBuildSteps.RemarkStep,
AuditSaveBuildSteps.AuditStatusDescStep,
AuditSaveBuildSteps.BuildStep {
private final AuditSaveVO auditSaveVO = new AuditSaveVO();
private AuditSaveVOBuilder() {}
public static AuditSaveBuildSteps.BizOrderStep create() {
return new AuditSaveVOBuilder();
}
@Override
public AuditSaveBuildSteps.AuditNodeStep withBizOrder(String bizOrderNo, BizTypeEnum bizType) {
auditSaveVO.bizOrderNo(bizOrderNo);
auditSaveVO.bizType(bizType);
return this;
}
// 其他接口实现...
}
步骤三:简化VO类
// AuditSaveVO.java - 简化后的值对象
@Getter
@Setter
public class AuditSaveVO implements Serializable {
// 字段定义
/**
* ★★★ 提供构建器入口方法 ★★★
*/
public static AuditSaveBuildSteps.BizOrderStep builder() {
return AuditSaveVOBuilder.create();
}
}
需要说明的一点是:将Builder从VO中分离,破坏了AuditSaveVO的field的可访问性。————我们不希望AuditSaveVO具有public的setter,因为我们期望外部使用builder模式来构建对象。 为了达到这次重构的目的,特做了一个折中,将AuditSaveVO的field设置为默认的package-private。
重构带来的收益
1. 职责分离更加彻底
- VO类:行数减少68%,只包含字段定义和基本方法
- 构建器类:独立存在,便于扩展和维护
- 接口定义:明确构建契约,支持多种实现
2. 测试策略更加灵活
// 可以独立测试构建器
@Test
void testBuilderWithBizOrder() {
AuditSaveVOBuilder builder = new AuditSaveVOBuilder();
AuditSaveBuildSteps.AuditNodeStep nextStep = builder.withBizOrder("test", BizTypeEnum.AGENT_CHANGE);
assertNotNull(nextStep);
}
// 可以Mock构建器
@Mock
private AuditSaveBuildSteps.BizOrderStep builderMock;
3. 依赖管理更加清晰
使用端 → AuditSaveBuildSteps(接口) ← AuditSaveVOBuilder(实现)
↑
AuditSaveVO(值对象)
这种依赖关系确保了:
- 使用端只依赖接口契约
- VO不依赖具体构建逻辑
- 构建器实现依赖接口和VO
结论
业务语义化的Builder模式通过巧妙的接口设计和步骤约束,彻底解决了复杂对象构建中的业务语义表达问题。这种程序设计思维的本身,正是高质量软件开发的精髓所在。
而通过将Builder从VO中分离的架构优化,我们进一步获得了更清晰的职责划分、更好的可测试性和更强的扩展能力。
这种Builder模式特别适合以下场景:
- 具有复杂业务规则的对象构建
- 需要强制构建顺序的业务场景
- 对代码质量和可维护性要求较高的项目
- 大型团队协作开发的项目
当看到一些不好的代码时,会发现我还算优秀;当看到优秀的代码时,也才意识到持续学习的重要!--buguge
本文来自博客园,转载请注明原文链接:https://www.cnblogs.com/buguge/p/19080855
还在用传统的builder模式来实例化构建对象吗?来,给你的程序注入点新鲜血液🩸!
浙公网安备 33010602011771号