Domanda

I'm trying to create a builder pattern that uses generics to provide type checking on some of the methods. Currently I have the following working:

ParameterBuilder.start(String.class).setName("foo").setDefaultValue("Hello").build();
ParameterBuilder.start(Integer.class).setName(bar).setDefaultValue(42).build();
ParameterBuilder.start(Boolean.class).setName(bar).setDefaultValue(false).build();

Using the code:

public class ParameterBuilder<T> {
  private String name;
  private T defaultValue;

  public static <T2> ParameterBuilder<T2> start(Class<T2> type) {
    return new ParameterBuilder<T2>();
  }
  // Other methods excluded for example
}

So the type of the input for the setDefaultValue method is defined by what's passed into the start method, just as I want.

But now I want to extend what's being passed into start() to contain a little more information. Essentially I want to pass in a "type" for the parameters I creating. Sometimes these parameters will be things like "email", "url" etc. The default value will still be of a known type (String in those cases), so I'd like to have something like:

ParameterBuilder.start(EMAIL).setName("email").setDefaultValue("foo@bar.com").build();
ParameterBuilder.start(URL).setName("website").setDefaultValue("http://www.somewhere.com").build();

Where at the moment EMAIL & URL are enums, containing amongst other things - the class of the default value. But if I go down this route, how would I instantiate the parameter builder?

public static <T2> ParameterBuilder<T2> start(ParameterType paramType) {
  Class<T2> type = paramType.getTypeClass();
  // How do I instantiate my ParameterBuilder with the right type?
}

If it can't be done using enums (which I can see being the case), does anyone have a suggestion for a different solution?

È stato utile?

Soluzione

I think you need one enum per class type (I don't see how you could have one enum cover several types and keep the thing working). In that case, a common generic interface could do what you want. You can then create some sort of factory to provide the enum constants if that helps.

This compiles:

static interface ParameterType<T> {}

static enum ParameterTypeEnum implements ParameterType<String> { EMAIL; }

public static void main(String[] args) {
    ParameterBuilder
           .start(ParameterTypeEnum.EMAIL)
           .setName("email")
           .setDefaultValue("foo@bar.com")
           .build();
}

public static class ParameterBuilder<T> {

    private String name;
    private T defaultValue;

    public static <T2> ParameterBuilder<T2> start(ParameterType<T2> paramType) {
        return new ParameterBuilder<T2>();
    }

    ParameterBuilder<T> setName(String name) {
        this.name = name;
        return this;
    }

    ParameterBuilder<T> setDefaultValue(T defaultValue) {
        this.defaultValue = defaultValue;
        return this;
    }

    void build() {}
}

Altri suggerimenti

I'm not sure the context in what you want to use this, but I think the following might be an option.

You can follow the Open/Closed principle and create an interface Parameter and have one implementation per type. The benefit of this, is that you don't need to add a new enum value for each new Parameter you want. You can later pass the class to ParameterBuilder rather than the enum and the ParameterBuilder and Parameter would work together to build what you need.

So ParameterBuilder.start() could return an instance of the specific Parameter and the parameter might have different methods depending on the type of parameter.

I don't think this answer is really good, but hopefully can give you a hint in how to build a potential solution for your context.

You could create an object hierachie for these Email and Url types

public class DefaultType {
    protected String name;
    protected String defaultValue;
    //some constructor
}

public class EmailType extends DefaultType {
    ...
}

public class URLType extends DefaultType {
    ...
}

then the parameter builder could look something like this:

public static ParameterBuilder start(DefaultType type) {
    ParameterBuilder builder = new ParameterBuilder(type);
    builder.setType(type);
    return builder;
}

Then you could call it like this:

ParameterBuilder.start(new EmailType("name","value");...

does this help or dont you want to go in this direction?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top