2016-07-19 13 views
2

Еще один день и еще одна борьба с дженериками.Java свободно строитель и наследование

Я набор Control объектов с помощью следующего дерева наследования:

BaseControl 
|_SimpleControl 
    |_MultipleControl 
    |_AutocompleteControl 
    |_SelectControl 

Для каждого из неабстрактного объекта в этом дереве я хотел бы предоставить строитель, так что эти объекты могут быть созданы легко. Вот то, что я до сих пор:

BaseControlBuilder:

public abstract class BaseControlBuilder<C extends BaseControl, B extends BaseControlBuilder<C, B>> { 
    protected C control; 
    private B builder; 

    BaseControlBuilder() { 
     control = createObj(); 
     builder = getThis(); 
    } 
    public C build() { return control; } 

    protected abstract C createObj(); 
    protected abstract B getThis(); 
} 

SimpleControlBuilder:

public class SimpleControlBuilder<C extends SimpleControl, B extends SimpleControlBuilder<C, B>> 
     extends BaseControlBuilder<SimpleControl, SimpleControlBuilder<C, B>> { 

    public SimpleControlBuilder(final String id, final String caption, 
      final InputType controlType) { 
     super(); 
     control.setId(id); 
     control.setCaption(caption); 
     control.setType(controlType); 
    } 

    public SimpleControlBuilder(final InputType controlType) { 
     this("", "", controlType); 
    } 

    public SimpleControlBuilder(final Enum<?> en, final InputType controlType) { 
     this(en.name(), en.toString(), controlType); 
    } 

    public SimpleControlBuilder<C, B> disabled() { 
     control.setDisabled(true); 
     return this; 
    } 

    @Override 
    protected SimpleControl createObj() { 
     return new SimpleControl(); 
    } 

    @Override 
    protected SimpleControlBuilder<C, B> getThis() { 
     return this; 
    } 
} 

MultipleControlBuilder:

abstract class MultipleControlBuilder<C extends MultipleControl, B extends MultipleControlBuilder<C, B>> 
     extends SimpleControlBuilder<MultipleControl, MultipleControlBuilder<C, B>> { 

    MultipleControlBuilder(final InputType type) { 
     super(type); 
    } 

    MultipleControlBuilder(final String id, final String caption, 
      final InputType type) { 
     super(id, caption, type); 
    } 

    MultipleControlBuilder(final Enum<?> en, final InputType type) { 
     super(en, type); 
    } 

    public MultipleControlBuilder<C, B> multiple() { 
     ((MultipleControl) control).setMultiple(true); 
     return this; 
    } 
} 

AutocompleteControlBuilder:

public class AutocompleteControlBuilder<C extends AutocompleteControl, B extends AutocompleteControlBuilder<C, B>> 
    extends MultipleControlBuilder<AutocompleteControl, AutocompleteControlBuilder<C, B>> { 

    public AutocompleteControlBuilder(final String url, 
      final AutocompleteType autocompleteType) { 
     this("", "", url, autocompleteType); 
    } 

    public AutocompleteControlBuilder(final String id, 
      final String caption, final String url, 
      final AutocompleteType autocompleteType) { 
     super(id, caption, InputType.AUTOCOMPLETE); 
     ((AutocompleteControl) control).setAutocompleteUrl(url); 
     ((AutocompleteControl) control).setAutocompleteType(autocompleteType); 
    } 

    public AutocompleteControlBuilder(final Enum<?> en, final String url, 
      final AutocompleteType autocompleteType) { 
     this(en.name(), en.toString(), url, autocompleteType); 
    } 

    @Override 
    protected AutocompleteControl createObj() { 
     return new AutocompleteControl(); 
    } 

    @Override 
    protected AutocompleteControlBuilder<C, B> getThis() { 
     return this; 
    } 
} 

Но удивительно, что у меня есть неожиданные результаты.
Например, в следующем коде я должен бросить control в MultipleControl назвать сеттер, несмотря на то, что C extends MultipleControl ...

Кроме того, следующий build() вызов метода: new AutocompleteControlBuilder<AutocompleteControl, AutocompleteControlBuilder>("url", AutocompleteType.STANDARD).build()); возвращает SimpleControl вместо AutocompleteControl, который не делает смысл, потому что я явно предоставил параметр типа.

И последняя солома - это то, что краткость и четкий код, который я пытаюсь достичь, убиты уродливым вызовом конструктора new AutocompleteControlBuilder<AutocompleteControl, AutocompleteControlBuilder>. Может ли кто-нибудь указать мне на лучшие методы решения этой проблемы?

+0

Могу ли я спросить, почему у вас есть поле '' 'builder'''? Я не вижу, как вы его используете. Java уже имеет ковариантные типы возвращаемых данных, на первый взгляд, я бы сказал, что вы можете удалить оба параметра типа, поскольку оба они кажутся деталями реализации. Это действительно зависит от использования, хотя ... –

+0

У меня есть быстрый вопрос об этой настройке @ mr.nothing, вам действительно нужно, чтобы SimpleControl был не абстрактный? – EpicPandaForce

+0

Хм, JavaFX тоже начал с строителей, и в наши дни без них. Так что строители _can_ имеют стилистические недостатки. В вашем случае: меньше конструкторов, фабричный метод в самом классе управления ('RadioButton.create(). Label (« not me »).build(): ') –

ответ

1

Хорошо, для того, чтобы правильно установить это, вы должны внести некоторые изменения:

public class SimpleControlBuilder<C extends SimpleControl, B extends SimpleControlBuilder<C, B>> 
     extends BaseControlBuilder<SimpleControl, SimpleControlBuilder<C, B>> { // this should extend with the extension classes 

    public SimpleControlBuilder(final String id, final String caption, 
      final InputType controlType) { 
     super(); 
     control.setId(id); 
     control.setCaption(caption); 
     control.setType(controlType); 
    } 

    public SimpleControlBuilder(final InputType controlType) { 
     this("", "", controlType); 
    } 

    public SimpleControlBuilder(final Enum<?> en, final InputType controlType) { 
     this(en.name(), en.toString(), controlType); 
    } 

    public SimpleControlBuilder<C, B> disabled() { // this should return B 
     control.setDisabled(true); 
     return this; 
    } 

    @Override 
    protected SimpleControl createObj() { // this should return C 
     return new SimpleControl(); 
    } 

    @Override 
    protected SimpleControlBuilder<C, B> getThis() { // this should return B 
     return this; 
    } 
} 

Так что означает

public abstract class SimpleControlBuilder<C extends SimpleControl, B extends SimpleControlBuilder<C, B>> 
     extends BaseControlBuilder<C, B> { 

    public SimpleControlBuilder(final String id, final String caption, 
      final InputType controlType) { 
     super(); 
     control.setId(id); 
     control.setCaption(caption); 
     control.setType(controlType); 
    } 

    public SimpleControlBuilder(final InputType controlType) { 
     this("", "", controlType); 
    } 

    public SimpleControlBuilder(final Enum<?> en, final InputType controlType) { 
     this(en.name(), en.toString(), controlType); 
    } 

    public B disabled() { 
     control.setDisabled(true); 
     return getThis(); 
    } 
} 

И

abstract class MultipleControlBuilder<C extends MultipleControl, B extends MultipleControlBuilder<C, B>> 
     extends SimpleControlBuilder<C, B> { 

    MultipleControlBuilder(final InputType type) { 
     super(type); 
    } 

    MultipleControlBuilder(final String id, final String caption, 
      final InputType type) { 
     super(id, caption, type); 
    } 

    MultipleControlBuilder(final Enum<?> en, final InputType type) { 
     super(en, type); 
    } 

    public B multiple() { 
     control.setMultiple(true); 
     return getThis(); 
    } 
} 

И

public abstract class AutocompleteControlBuilder<C extends AutocompleteControl, B extends AutocompleteControlBuilder<C, B>> 
    extends MultipleControlBuilder<C, B>> { 

    public AutocompleteControlBuilder(final String url, 
      final AutocompleteType autocompleteType) { 
     this("", "", url, autocompleteType); 
    } 

    public AutocompleteControlBuilder(final String id, 
      final String caption, final String url, 
      final AutocompleteType autocompleteType) { 
     super(id, caption, InputType.AUTOCOMPLETE); 
     control.setAutocompleteUrl(url); 
     control.setAutocompleteType(autocompleteType); 
    } 

    public AutocompleteControlBuilder(final Enum<?> en, final String url, 
      final AutocompleteType autocompleteType) { 
     this(en.name(), en.toString(), url, autocompleteType); 
    } 
} 

Это работает, если MultipleControl extends SimpleControl и AutocompleteControl extends MultipleControl, и у вас есть конкретные удлинения SimpleControl, которые могут возвращаться на getThis() с конкретизированными параметрами.

public class SomeControlBuilder extends MultipleControlBuilder<SomeControl, SomeControlBuilder> { 
    public SomeControlBuilder(final InputType type) { 
     super(type); 
    } 

    public SomeControlBuilder(final String id, final String caption, 
      final InputType type) { 
     super(id, caption, type); 
    } 

    public SomeControlBuilder(final Enum<?> en, final InputType type) { 
     super(en, type); 
    } 

    @Override 
    protected SomeControlBuilder getThis() { 
     return this; 
    } 

    @Override 
    protected SomeControl createObj() { 
     return new SomeControl(); 
    } 
} 
+0

Имею ли я это право, что нет элегантного способа решить это? Будут все еще ужасные вызовы конструктора с параметрами типа, которые могут быть * логически * опущены. –

+0

ну, я действительно не знаю, что делают эти параметры. Скорее всего, вам нужно сохранить 3 конструктора по иерархии, если вы сохраните эти конструкторы, да. Я думаю, что дженерики были значительно упрощены, хотя, я думаю, это довольно элегантно. – EpicPandaForce