2016-07-19 5 views
2

Ein weiterer Tag und noch ein Kampf mit Generika.Java fließenden Builder und Vererbung

Ich habe von Control Objekten mit dem folgenden Vererbungsbaum gesetzt:

BaseControl 
|_SimpleControl 
    |_MultipleControl 
    |_AutocompleteControl 
    |_SelectControl 

Für jeden nicht-abstraktes Objekt in diesem Baum Ich mag würde Builder zur Verfügung zu stellen, damit diese Objekte einfach erstellt werden. Hier ist, was ich bisher:

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

Aber überraschenderweise habe ich einige unerwartete Ergebnisse.
Zum Beispiel in dem folgenden Code habe ich control zu MultipleControl werfen Setter trotz der Tatsache zu nennen, dass C extends MultipleControl ...

Darüber hinaus wird der folgende build() Methodenaufruf: new AutocompleteControlBuilder<AutocompleteControl, AutocompleteControlBuilder>("url", AutocompleteType.STANDARD).build()); kehrt SimpleControl statt AutocompleteControl, die nicht macht Sinn, weil ich explizit Typparameter angegeben habe.

Und der letzte Strohhalm ist, dass Prägnanz und klaren Code, den ich versuche zu erreichen, durch hässliche new AutocompleteControlBuilder<AutocompleteControl, AutocompleteControlBuilder> Konstruktor Anruf getötet werden. Könnte mich jemand auf die besten Praktiken zur Lösung dieses Problems hinweisen?

+0

Darf ich fragen, warum Sie das '' 'builder''' Feld haben? Ich sehe nicht, dass du es benutzt. Java hat bereits kovariante Rückgabetypen, auf den ersten Blick würde ich sagen, dass Sie beide Typparameter entfernen könnten, da beide Implementierungsdetails zu sein scheinen. Es hängt wirklich von der Verwendung ab ... –

+0

Ich habe eine schnelle Frage zu diesem Setup @ mr.nothing, brauchst du eigentlich 'SimpleControl' um nicht-abstrakt zu sein? – EpicPandaForce

+0

Hmm, JavaFX begann auch mit Bauarbeitern und verzichtet heutzutage auf sie. Builder können also stilistische Nachteile haben. In Ihrem Fall: weniger Konstruktoren, eine Factory-Methode in der Steuerelementklasse selbst ('RadioButton.create(). Label (" not me ").build(): ') –

Antwort

1

Okay, um diese korrekt einzurichten, sollten Sie einige Änderungen vornehmen:

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

Das heißt also,

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

Und

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

Und

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

Dies funktioniert, wenn MultipleControl extends SimpleControl und AutocompleteControl extends MultipleControl, und Sie haben konkrete Erweiterungen von SimpleControl, die sich mit getThis() mit konkretisierten Parameter zurückgeben können.

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

Bekomme ich das richtig, dass es keine elegante Möglichkeit gibt, das zu lösen? Es wird immer noch schreckliche Constructor-Aufrufe mit Typparametern geben, die * logisch * weggelassen werden können. –

+0

Nun, ich weiß nicht wirklich was diese Parameter tun. Sie müssen wahrscheinlich die 3 Konstruktoren in der Hierarchie beibehalten, wenn Sie diese Konstruktoren behalten, ja. Ich denke, die Generika wurden stark vereinfacht, ich denke, es ist ziemlich elegant. – EpicPandaForce