步骤生成器模式使用委托和枚举?

问题描述 投票:5回答:3

我有这个项目,我正在努力,基本上这就是我想要实现的目标。

这就是我所拥有的:

MyObject obj = MyObject.builder()
                       .withValue("string")
                       .withAnotherValue("string")
                       .build();

MyObject obj = MyObject.builder()
                       .withValue("string")
                       .withAnotherValue("string")
                       .withField("key", "value")
                       .build();

因此,步骤构建器模式强制用户按顺序使用withValue()方法和withAnotherValue()方法。方法field()是可选的,可以根据需要多次使用。我跟着这个网站例如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;
    }
  }

}
java design-patterns enums builder delegation
3个回答
0
投票

考虑到您正在为特定类型的构建器寻找特定方法,具有多个构建器,可以构建的每种类型的MyObject都可以使用。您可以创建一个定义构建器的接口,然后将公共功能放入一个抽象类中,各个构建器将从该类中扩展。例如:

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();

1
投票

使用enum是不可能的,但你可以使用类似自定义enum的类来做到这一点:

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();
    }
}

您可以轻松地将该方法应用于步骤构建器,但这里有一个单独的问题。由于步骤构建器中的每个步骤都指定了返回类型中的下一步,因此您无法轻松地重复使用步骤接口。例如,您需要声明单独的接口RockValueStepStoneValueStep等,因为接口本身指定了步骤顺序。

唯一简单的方法是,如果单独的类型(岩石,石头等)仅严格地添加步骤,例如, Type.ROCK返回ColourStepType.STONE返回WeightStepColourStepWeightStep返回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上声明一个抽象方法并使用协变返回类型覆盖它,但协变返回类型永远不可见: // 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(); }

0
投票

您的问题可以概括如下:“我如何编写以下方法?”

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的答案,这也很有效。

© www.soinside.com 2019 - 2024. All rights reserved.