使用委托和枚举的步骤生成器模式?
Step builder pattern using delegation and enums?
我正在从事这个项目,基本上这就是我想要实现的目标。
这是我的:
MyObject obj = MyObject.builder()
.withValue("string")
.withAnotherValue("string")
.build();
MyObject obj = MyObject.builder()
.withValue("string")
.withAnotherValue("string")
.withField("key", "value")
.build();
因此步骤构建器模式强制用户按顺序使用 withValue()
方法和 withAnotherValue()
方法。方法 field()
是可选的,可以多次使用,例如 want.I 关注此网站 http://www.svlada.com/step-builder-pattern/
所以我想实现的是:
MyObject obj = MyObject.builder(Type.ROCK)
.withColour("blue")
.withValue("string")
.withAnotherValue("string")
.build();
MyObject obj = MyObject.builder(Type.STONE)
.withWeight("heavy")
.withValue("string")
.withAnotherValue("string")
.withField("key", "value")
.build();
因此,在 builder() 方法中,您将放置一个枚举类型,并根据该枚举显示一组不同的方法。因此对于 ROCK,withValue()
、withAnotherValue()
和 withColour()
现在是强制性的。但对于 STONE withWeight()
,withAnotherValue()
和 withColour()
是强制性的。
我这样的事情可能吗?在过去的两天里,我一直在尝试解决这个问题,但我似乎无法让它为每种类型提供特定的方法。它只是显示了 Builder 中的所有方法。
非常感谢任何想法和帮助。
代码:
枚举
public enum Type implements ParameterType<Type> {
ROCK, STONE
}
参数类型
interface ParameterType<T> {}
MyObject
public class MyObject implements Serializable {
private static final long serialVersionUID = -4970453769180420689L;
private List<Field> fields = new ArrayList<>();
private MyObject() {
}
public interface Type {
Value withValue(String value);
}
public interface Value {
Build withAnotherValue(String anotherValue);
}
public interface Build {
MyObject build();
}
public Type builder(Parameter type) {
return new Builder();
}
public static class Builder implements Build, Type, Value {
private final List<Field> fields = new ArrayList<>();
@Override
public Build withAnotherValue(String anotherValue) {
fields.add(new Field("AnotherValue", anotherValue));
return this;
}
@Override
public Value withValue(String value) {
fields.add(new Field("Value", value));
return this;
}
@Override
public MyObject build() {
MyObject myObject = new MyObject();
myObject.fields.addAll(this.fields);
return myObject;
}
}
}
使用 enum
不可能做到这一点,但您可以使用自定义 enum
来做到这一点,例如 class:
public final class Type<B extends MyObject.Builder> {
private final Supplier<? extends B> supplier;
private Type(Supplier<? extends B> supplier) {
this.supplier = Objects.requireNonNull(supplier);
}
public B builder() {
return supplier.get();
}
public static final Type<MyObject.RockBuilder> ROCK =
new Type<>(MyObject.RockBuilder::new);
public static final Type<MyObject.StoneBuilder> STONE =
new Type<>(MyObject.StoneBuilder::new);
}
public class MyObject {
// ...
// And this method is probably superfluous at this point.
public static <B extends MyObject.Builder> builder(Type<? extends B> type) {
return type.builder();
}
}
您可以轻松地将这种方法应用于步骤构建器,但这里有一个单独的问题。由于步骤构建器中的每个步骤都指定 return 类型中的下一步,因此您不能非常轻松地 re-use 步骤接口。例如,您需要声明单独的接口 RockValueStep
、StoneValueStep
等,因为接口本身指定了步骤顺序。
唯一简单的解决方法是,如果单独的类型(岩石、石头等)仅严格添加步骤,例如Type.ROCK
return 一个 ColourStep
和 Type.STONE
return 一个 WeightStep
,并且 ColourStep
和 WeightStep
return ValueStep
:
// Rock builder starts here.
interface ColourStep { ValueStep withColour(String c); }
// Stone builder starts here.
interface WeightStep { ValueStep withWeight(String w); }
// Shared.
interface ValueStep { AnotherValueStep withValue(String v); }
然后:
public final class Type<B /* extends ABuilderStepMarker, possibly */> {
// (Constructor and stuff basically same as before.)
public static final Type<MyObject.ColourStep> ROCK =
new Type<>(/* implementation */::new);
public static final Type<MyObject.WeightStep> STONE =
new Type<>(/* implementation */::new);
}
这种事情不能用 enum
完成的原因有很多:
enum
不能通用:
// This is an error.
enum Type<T> {
}
尽管您可以在 enum
上声明抽象方法并使用协变 return 类型覆盖它,但协变 return 类型永远不可见:
// This is valid code, but the actual type of
// Type.ROCK is just Type, so the return type of
// Type.ROCK.builder() is just MyObject.Builder,
// despite the override.
enum Type {
ROCK {
@Override
public MyObject.RockBuilder builder() {
return new MyObject.RockBuilder();
}
};
public abstract MyObject.Builder builder();
}
考虑到您正在为特定类型的构建器寻找特定方法,拥有多个构建器,每个构建器对应一种可构建的 MyObject
类型可能效果最好。您可以创建一个定义构建器的接口,然后将通用功能放入抽象 class 中,各个构建器从中扩展。例如:
public interface Builder {
public MyObject build();
}
public abstract class AbstractBuilder() {
private final List<Field> fields = new ArrayList<>();
protected void addField(String key, String value) {
fields.add(new Field(key, value));
}
@Override
public MyObject build() {
MyObject myObject = new MyObject();
myObject.fields.addAll(this.fields);
return myObject;
}
}
public class StoneBuilder extends AbstractBuilder {
public StoneBuilder withValue(String value) {
addField("Value", value);
return this;
}
// ...More builder methods...
}
public class RockBuilder extends AbstractBuilder {
public RockBuilder withAnotherValue(String value) {
addField("AnotherValue", value);
return this;
}
// ...More builder methods...
}
这允许您以下列方式构建 MyObject
个实例:
MyObject obj = new RockBuilder()
.withValue("string")
.build();
MyObject obj = new StoneBuilder()
.withAnotherValue("string")
.build();
您的问题可以概括如下:"How can I write the following method?"
public <T extends AbstractBuilder> T builder(final SomeNonGenericObject object) {
// code goes here
}
答案是:“你不能,因为 编译器 无法推断出 T
的类型。唯一的方法就是这个可能是以某种方式将 T
作为参数传递:
public <T extends AbstractBuilder> T builder(final SomeNonGenericObject object, final Class<T> builderClass) {
// code goes here
}
或
public <T extends AbstractBuilder> T builder(final SomeGenericObject<T> object) {
// code goes here
}
例如:
public <T extends AbstractBuilder> T builder(final Supplier<T> object) {
return supplier.get();
}
final Supplier<AbstractBuilder> rockBuilderSupplier = RockBuilder::new;
builder(rockBuilerSupplier)
.withColour("blue")
// etc
或者简单地使用 Justin Albano 的答案,同样有效。
我正在从事这个项目,基本上这就是我想要实现的目标。
这是我的:
MyObject obj = MyObject.builder()
.withValue("string")
.withAnotherValue("string")
.build();
MyObject obj = MyObject.builder()
.withValue("string")
.withAnotherValue("string")
.withField("key", "value")
.build();
因此步骤构建器模式强制用户按顺序使用 withValue()
方法和 withAnotherValue()
方法。方法 field()
是可选的,可以多次使用,例如 want.I 关注此网站 http://www.svlada.com/step-builder-pattern/
所以我想实现的是:
MyObject obj = MyObject.builder(Type.ROCK)
.withColour("blue")
.withValue("string")
.withAnotherValue("string")
.build();
MyObject obj = MyObject.builder(Type.STONE)
.withWeight("heavy")
.withValue("string")
.withAnotherValue("string")
.withField("key", "value")
.build();
因此,在 builder() 方法中,您将放置一个枚举类型,并根据该枚举显示一组不同的方法。因此对于 ROCK,withValue()
、withAnotherValue()
和 withColour()
现在是强制性的。但对于 STONE withWeight()
,withAnotherValue()
和 withColour()
是强制性的。
我这样的事情可能吗?在过去的两天里,我一直在尝试解决这个问题,但我似乎无法让它为每种类型提供特定的方法。它只是显示了 Builder 中的所有方法。
非常感谢任何想法和帮助。
代码:
枚举
public enum Type implements ParameterType<Type> {
ROCK, STONE
}
参数类型
interface ParameterType<T> {}
MyObject
public class MyObject implements Serializable {
private static final long serialVersionUID = -4970453769180420689L;
private List<Field> fields = new ArrayList<>();
private MyObject() {
}
public interface Type {
Value withValue(String value);
}
public interface Value {
Build withAnotherValue(String anotherValue);
}
public interface Build {
MyObject build();
}
public Type builder(Parameter type) {
return new Builder();
}
public static class Builder implements Build, Type, Value {
private final List<Field> fields = new ArrayList<>();
@Override
public Build withAnotherValue(String anotherValue) {
fields.add(new Field("AnotherValue", anotherValue));
return this;
}
@Override
public Value withValue(String value) {
fields.add(new Field("Value", value));
return this;
}
@Override
public MyObject build() {
MyObject myObject = new MyObject();
myObject.fields.addAll(this.fields);
return myObject;
}
}
}
使用 enum
不可能做到这一点,但您可以使用自定义 enum
来做到这一点,例如 class:
public final class Type<B extends MyObject.Builder> {
private final Supplier<? extends B> supplier;
private Type(Supplier<? extends B> supplier) {
this.supplier = Objects.requireNonNull(supplier);
}
public B builder() {
return supplier.get();
}
public static final Type<MyObject.RockBuilder> ROCK =
new Type<>(MyObject.RockBuilder::new);
public static final Type<MyObject.StoneBuilder> STONE =
new Type<>(MyObject.StoneBuilder::new);
}
public class MyObject {
// ...
// And this method is probably superfluous at this point.
public static <B extends MyObject.Builder> builder(Type<? extends B> type) {
return type.builder();
}
}
您可以轻松地将这种方法应用于步骤构建器,但这里有一个单独的问题。由于步骤构建器中的每个步骤都指定 return 类型中的下一步,因此您不能非常轻松地 re-use 步骤接口。例如,您需要声明单独的接口 RockValueStep
、StoneValueStep
等,因为接口本身指定了步骤顺序。
唯一简单的解决方法是,如果单独的类型(岩石、石头等)仅严格添加步骤,例如Type.ROCK
return 一个 ColourStep
和 Type.STONE
return 一个 WeightStep
,并且 ColourStep
和 WeightStep
return ValueStep
:
// Rock builder starts here.
interface ColourStep { ValueStep withColour(String c); }
// Stone builder starts here.
interface WeightStep { ValueStep withWeight(String w); }
// Shared.
interface ValueStep { AnotherValueStep withValue(String v); }
然后:
public final class Type<B /* extends ABuilderStepMarker, possibly */> {
// (Constructor and stuff basically same as before.)
public static final Type<MyObject.ColourStep> ROCK =
new Type<>(/* implementation */::new);
public static final Type<MyObject.WeightStep> STONE =
new Type<>(/* implementation */::new);
}
这种事情不能用 enum
完成的原因有很多:
enum
不能通用:// This is an error. enum Type<T> { }
尽管您可以在
enum
上声明抽象方法并使用协变 return 类型覆盖它,但协变 return 类型永远不可见:// This is valid code, but the actual type of // Type.ROCK is just Type, so the return type of // Type.ROCK.builder() is just MyObject.Builder, // despite the override. enum Type { ROCK { @Override public MyObject.RockBuilder builder() { return new MyObject.RockBuilder(); } }; public abstract MyObject.Builder builder(); }
考虑到您正在为特定类型的构建器寻找特定方法,拥有多个构建器,每个构建器对应一种可构建的 MyObject
类型可能效果最好。您可以创建一个定义构建器的接口,然后将通用功能放入抽象 class 中,各个构建器从中扩展。例如:
public interface Builder {
public MyObject build();
}
public abstract class AbstractBuilder() {
private final List<Field> fields = new ArrayList<>();
protected void addField(String key, String value) {
fields.add(new Field(key, value));
}
@Override
public MyObject build() {
MyObject myObject = new MyObject();
myObject.fields.addAll(this.fields);
return myObject;
}
}
public class StoneBuilder extends AbstractBuilder {
public StoneBuilder withValue(String value) {
addField("Value", value);
return this;
}
// ...More builder methods...
}
public class RockBuilder extends AbstractBuilder {
public RockBuilder withAnotherValue(String value) {
addField("AnotherValue", value);
return this;
}
// ...More builder methods...
}
这允许您以下列方式构建 MyObject
个实例:
MyObject obj = new RockBuilder()
.withValue("string")
.build();
MyObject obj = new StoneBuilder()
.withAnotherValue("string")
.build();
您的问题可以概括如下:"How can I write the following method?"
public <T extends AbstractBuilder> T builder(final SomeNonGenericObject object) {
// code goes here
}
答案是:“你不能,因为 编译器 无法推断出 T
的类型。唯一的方法就是这个可能是以某种方式将 T
作为参数传递:
public <T extends AbstractBuilder> T builder(final SomeNonGenericObject object, final Class<T> builderClass) {
// code goes here
}
或
public <T extends AbstractBuilder> T builder(final SomeGenericObject<T> object) {
// code goes here
}
例如:
public <T extends AbstractBuilder> T builder(final Supplier<T> object) {
return supplier.get();
}
final Supplier<AbstractBuilder> rockBuilderSupplier = RockBuilder::new;
builder(rockBuilerSupplier)
.withColour("blue")
// etc
或者简单地使用 Justin Albano 的答案,同样有效。