Template builder template using delegation and enumerations?

I have this project that I am working on, and basically this is what I would like to achieve.

This is what I have:

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

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

Thus, the step builder pattern forces the user to use the method withValue()and method withAnotherValue()in that order. The method field()is optional and can be used as many times as you want. I followed this site, for example http://www.svlada.com/step-builder-pattern/

So, I would like to achieve this:

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.

.

:

Enum

public enum Type implements ParameterType<Type> {

  ROCK, STONE

}

ParameterType

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

}
+4
3

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

enum, enum -like:

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

, . , . , , RockValueStep, StoneValueStep .., .

, (, ..) , , , Type.ROCK a ColourStep Type.STONE a WeightStep, ColourStep WeightStep 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

Your question can be summarized as follows: "How can I write the following method?"

public <T extends AbstractBuilder> T builder(final SomeNonGenericObject object) {
    // code goes here
}

And the answer: “You cannot, because the compiler cannot determine what a type is T. The only way this is possible is to somehow pass Tas a parameter:

public <T extends AbstractBuilder> T builder(final SomeNonGenericObject object, final Class<T> builderClass) {
    // code goes here
}

or

public <T extends AbstractBuilder> T builder(final SomeGenericObject<T> object) {
    // code goes here
}

For instance:

public <T extends AbstractBuilder> T builder(final Supplier<T> object) {
    return supplier.get();
}

final Supplier<AbstractBuilder> rockBuilderSupplier = RockBuilder::new;
builder(rockBuilerSupplier)
    .withColour("blue")
    // etc

Or just use Justin Albano's answer, which works just as well.

0
source

Source: https://habr.com/ru/post/1695767/


All Articles