buguge - Keep it simple,stupid

知识就是力量,但更重要的,是运用知识的能力why buguge?

导航

妙哉!这个业务语义化的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();

这些方式存在三个核心问题:

  1. 业务语义缺失:订单号bizOrderNo与业务类型bizType是紧密关联的业务概念,却被拆分为独立操作
  2. 构建顺序混乱:缺乏对构建步骤的强制约束,可能导致业务逻辑错误
  3. 参数校验困难:无法在编译期确保必填参数的完整性,这往往是生产运行期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;
        }
        
        // 其他接口实现...
    }
}

核心是三步:

  1. 接口步骤定义​(契约先行)
  2. 构建器实现​(业务内聚)--AuditSaveVOBuilder
  3. 值对象设计​- - 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();

设计精髓与业务价值

  1. 业务单元聚合设计

    // 将业务上紧密关联的字段组合在一起设置
    .withBizOrder("ORD001", BizTypeEnum.AGENT_CHANGE)
    

    这种方式保持了业务概念的完整性,避免了字段分散设置带来的语义割裂。业务意图更加明确,大大提升代码可读性。

  2. 构建流程强约束
    通过接口返回值类型限定,确保了构建步骤必须按照业务逻辑顺序执行:

    withBizOrder → withAuditNode → withAuditor → withRemark → withAuditStatusDesc → build
    

    有了这个强制约束,开发者再无需care、无需worry是否忘记给个别field赋值,从而提高开发效率。

  3. 编译期安全保证
    由于构建步骤的接口约束,开发者无法跳过必需步骤或颠倒顺序,大大减少了对象构建相关的BUG。

  4. 非必需字段可不赋值————灵活性的拿捏
    对于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主体中完全分离,实现更清晰的职责划分。

重构动机与目标

分离的目的:

  1. 单一职责原则:VO应专注于数据承载,Builder专注于对象构建
  2. 降低耦合度:避免VO类随着构建逻辑的复杂而膨胀
  3. 增强可测试性:构建逻辑可以独立测试
  4. 提高复用性:同一构建器可以支持多种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模式特别适合以下场景:

  • 具有复杂业务规则的对象构建
  • 需要强制构建顺序的业务场景
  • 对代码质量和可维护性要求较高的项目
  • 大型团队协作开发的项目

posted on 2025-09-08 22:55  buguge  阅读(31)  评论(0)    收藏  举报