【发布时间】:2021-11-16 23:00:08
【问题描述】:
我正在设计一个 java 系统,用户可以在其中以流畅的风格定义一些规则。
规则有许多属性,它们部分是相互排斥的。 我们使用带有验证的构建器模式。
为了使系统更易于使用,我们想引入一个 StepBuilder,以引导用户完成所有必要的步骤。
有不同类型的规则,但都有一些共同的属性。
当前系统:
abstract BaseBuilder<T extends BaseBuilder<T>> {
protected String property1;
protected String property2;
protected String property3;
abstract Rule build();
public T withProperty1(String data) {
this.property1 = data;
return this;
}
public T withProperty2(String data) {
this.property2 = data;
return this;
}
public T withProperty3(String data) {
//this should only be possible if property2 is not set or similar logic
this.property3 = data;
return this;
}
}
//there are a few others like this e.g. SpecialBuilder1-10
class SpecialRuleBuilder extends BaseBuilder<SpecialBuilder> {
protected String special1;
protected String special2;
public T withSpecial1(String data) {
this.special1 = data;
return this;
}
public T withSpecial2(String data) {
this.special2 = data;
return this;
}
@Override
Rule builder() {
return new SpecialRule(property1, property3, special1, special2, ....);
}
static SpecialRuleBuilder builder() {
return new SpecialRuleBuilder();
}
}
class BuilderTest() {
//User can set anything, no steps are enforced at compile time
Result result = SpecialBuilder.builder()
.property1("some")
.special2("thing")
.build();
}
我们如何使用包含层次结构(父类)的 StepBuilder,以便用户不能两次进入同一步骤 不能意外返回到上一个基本步骤并再次设置一些互斥属性.
理想情况下,用户不应该知道所有特殊的构建器,并且具有相同的入口点和引导步骤。例如:
Result result = GeneralBuilder.builder()
.withBaseProperty1("asdas") <-- Step 1
.withBaseProperty2("asd") <-- Step 2, Step 3 is now not visible, continue with all possible special options
.withSpecial1("asd") <-- no we are in the step of one concrete builder, and should not get "out" again, not even to the base methods
我知道如何定义接口步骤,我只是不知道如何在基本步骤的末尾包含特殊构建器的所有可能的开始步骤,因为更高的接口/类可能不应该依赖于较低的部分层次结构。
这有可能吗?
【问题讨论】:
-
“所以用户不能两次进入同一个步骤”您要避免的问题是什么?
-
应该逐步指导用户,应该没有(或很少)可能建立错误的规则 - 因为这只会在运行时出现。相同的步骤实际上不会有问题,但如果我在 SpecialBuilderStep 中并且可以再次调用基类的所有方法,我可以例如再次设置 base property3,即使我之前设置了 base property2 并且它们是互斥的。规则可以变得非常大,并且有数千条,因此它应该尽可能简单/安全。
-
我同意this answer 的第一句话:“这是个坏主意;您需要编写的代码数量之多令人震惊。”。我使用Error Prone 解决了一个类似的问题,使同一个setter 的多次调用成为编译器错误。因此,您可以编写代码,调用您喜欢的任何设置器,但它会在编译时失败。这不是一个“纯 Java”解决方案,但它比您尝试的要容易得多。
标签: java generics design-patterns interface builder-pattern