如何在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
相关产品推荐
相关产品推荐

