You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Builder设计模式中实现方法的条件可见性?

Builder模式限制方法调用顺序的实现方案

要实现调用顺序限制,最优方案是编译期强制校验,通过拆分不同阶段的接口来控制不同阶段可访问的方法,完全避免非法调用的可能,比运行时抛异常的方案体验好得多。

实现原理

我们把Builder的调用流程拆成两个阶段,每个阶段对应独立的接口:

  • 初始阶段:仅暴露a()方法,调用a()后会进入下一阶段
  • 调用a()后的阶段:才暴露b()方法,允许调用b()或者结束调用

代码实现

1. 定义阶段接口

// 初始阶段接口:仅允许调用a()方法
public interface InitialBuilder {
    PostABuilder a();
}

// 已调用a()后的阶段接口:允许调用b()方法
public interface PostABuilder {
    PostABuilder b();
    // 如果需要最终生成目标对象,也可以在这里定义build()方法
    // Target build();
}

2. 实现Builder实体类

public class ConcreteBuilder implements InitialBuilder, PostABuilder {
    // 存放业务参数
    private String configA;
    private String configB;

    // 构造方法私有,禁止外部直接实例化
    private ConcreteBuilder() {}

    // 对外唯一入口:返回初始阶段接口,强制用户只能先调用a()
    public static InitialBuilder create() {
        return new ConcreteBuilder();
    }

    @Override
    public PostABuilder a() {
        // 原有a()的业务逻辑
        this.configA = "a方法配置完成";
        return this;
    }

    @Override
    public PostABuilder b() {
        // 原有b()的业务逻辑
        this.configB = "b方法配置完成";
        return this;
    }
}

调用效果

合法调用可以正常编译执行:

// 合法调用1:仅调用a()
ConcreteBuilder.create().a();
// 合法调用2:先a再b
ConcreteBuilder.create().a().b();

所有非法调用在编码阶段就会被IDE提示错误,完全无法通过编译:

// 编译报错:InitialBuilder接口中不存在b()方法
ConcreteBuilder.create().b();
// 同上,第一步根本找不到b()方法,无法写出b().a()的调用链
ConcreteBuilder.create().b().a();

备选运行时实现(不推荐)

如果不想调整接口结构,也可以在b()方法中加状态校验,未调用a()就直接抛异常,但这种方案只能在运行时发现问题,体验较差:

public class Builder {
    private boolean hasCalledA = false;

    public Builder a() {
        hasCalledA = true;
        // 原有逻辑
        return this;
    }

    public Builder b() {
        if (!hasCalledA) {
            throw new IllegalStateException("必须先调用a()方法才能调用b()");
        }
        // 原有逻辑
        return this;
    }
}

内容的提问来源于stack exchange,提问作者Paul

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 13:57:01