如何用委托与枚举实现强制顺序的Step Builder模式?
Hey,这个需求用Step Builder模式 + 枚举状态机 + 委托来实现简直是完美匹配!既能强制要求用户按顺序调用必填方法,又能灵活支持可选方法的多次调用,而且代码结构清晰,还能在编译期就防止错误调用。我给你一步步拆解实现思路和完整代码:
核心思路
- 用枚举做状态管理:定义构建的各个阶段(比如需要设置value、需要设置anotherValue、可以添加字段),跟踪当前处于哪个步骤,确保调用顺序正确。
- 用阶段接口做类型限制:每个阶段对应一个接口,只暴露当前阶段允许调用的方法,这样用户在编译期就只能调用符合顺序的方法,跳步直接编译报错。
- 用委托类处理实际逻辑:写一个内部Builder实现类,实现所有阶段接口,负责实际的字段赋值和状态切换,对外只暴露当前阶段的接口类型,隐藏内部细节。
完整代码实现
import java.util.Collections; import java.util.HashMap; import java.util.Map; public class MyObject { // 最终对象的字段 private final String value; private final String anotherValue; private final Map<String, String> fields; // 私有构造方法,只能通过Builder创建 private MyObject(Builder builder) { this.value = builder.value; this.anotherValue = builder.anotherValue; this.fields = Collections.unmodifiableMap(new HashMap<>(builder.fields)); } // 对外提供getter public String getValue() { return value; } public String getAnotherValue() { return anotherValue; } public String getField(String key) { return fields.get(key); } // 构建入口:返回第一个阶段的接口 public static ValueStep builder() { return new MyObjectBuilderImpl(); } // ------------------------------ // 1. 枚举:标记当前构建阶段 // ------------------------------ private enum BuildStep { NEED_VALUE, // 等待设置value NEED_ANOTHER_VALUE,// 等待设置anotherValue CAN_ADD_FIELDS // 可添加字段(可选) } // ------------------------------ // 2. 阶段接口:强制暴露当前阶段允许的方法 // ------------------------------ public interface ValueStep { AnotherValueStep withValue(String value); } public interface AnotherValueStep { FieldStep withAnotherValue(String anotherValue); } public interface FieldStep { FieldStep withField(String key, String value); MyObject build(); } // ------------------------------ // 3. 核心Builder实现类:处理实际逻辑,委托的核心 // ------------------------------ private static class MyObjectBuilderImpl implements ValueStep, AnotherValueStep, FieldStep { private BuildStep currentStep = BuildStep.NEED_VALUE; private String value; private String anotherValue; private Map<String, String> fields = new HashMap<>(); @Override public AnotherValueStep withValue(String value) { validateStep(BuildStep.NEED_VALUE); this.value = value; currentStep = BuildStep.NEED_ANOTHER_VALUE; return this; // 返回当前实例,但类型是AnotherValueStep,只能调用该接口的方法 } @Override public FieldStep withAnotherValue(String anotherValue) { validateStep(BuildStep.NEED_ANOTHER_VALUE); this.anotherValue = anotherValue; currentStep = BuildStep.CAN_ADD_FIELDS; return this; } @Override public FieldStep withField(String key, String value) { validateStep(BuildStep.CAN_ADD_FIELDS); fields.put(key, value); return this; // 保持FieldStep类型,支持多次调用 } @Override public MyObject build() { validateStep(BuildStep.CAN_ADD_FIELDS); return new MyObject(this); } // 辅助方法:校验当前阶段是否正确,不正确则抛异常 private void validateStep(BuildStep expectedStep) { if (currentStep != expectedStep) { throw new IllegalStateException( String.format("当前阶段不允许该操作!预期阶段:%s,实际阶段:%s", expectedStep, currentStep) ); } } } }
使用示例
完全符合你想要的调用方式,而且顺序不对的话要么编译报错,要么运行时抛异常(不过编译期就能拦住大部分错误):
// 正常调用:必填方法按顺序,可选方法可多次调用 MyObject obj1 = MyObject.builder() .withValue("test string") .withAnotherValue("another test") .withField("user", "Alice") .withField("age", "25") .build(); // 也可以不调用可选方法 MyObject obj2 = MyObject.builder() .withValue("hello") .withAnotherValue("world") .build(); // 错误调用:编译期直接报错!因为builder()返回的是ValueStep,没有withAnotherValue方法 // MyObject wrongObj = MyObject.builder() // .withAnotherValue("跳过value直接调用") // .build();
为什么这种实现这么靠谱?
- 编译期强制顺序:通过阶段接口的类型限制,用户根本没法写出跳步的代码,比运行时检查友好太多。
- 灵活的可选方法:
withField()返回的是FieldStep本身,所以可以链式调用N次,完全满足需求。 - 扩展性强:以后要是需要加新的必填步骤,只需要新增枚举值和对应的阶段接口,修改BuilderImpl的状态切换逻辑就行,改动很小。
- 职责清晰:枚举管状态,接口管暴露的方法,BuilderImpl管实际逻辑,分工明确,代码可读性高。
内容的提问来源于stack exchange,提问作者Hexcii
相关产品推荐
相关产品推荐

