【问题标题】:Java fluent builder and inheritanceJava fluent 构建器和继承
【发布时间】:2016-07-19 10:24:26
【问题描述】:

又是一天,又是与泛型的斗争。

我有一组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;
    }
}

但令人惊讶的是,我得到了一些意想不到的结果。
例如,在下面的代码中,尽管C extends MultipleControl...

此外,以下build() 方法调用:new AutocompleteControlBuilder&lt;AutocompleteControl, AutocompleteControlBuilder&gt;("url", AutocompleteType.STANDARD).build()); 返回SimpleControl 而不是AutocompleteControl,这没有意义,因为我明确提供了类型参数。

最后一根稻草是我试图实现的简洁明了的代码被丑陋的new AutocompleteControlBuilder&lt;AutocompleteControl, AutocompleteControlBuilder&gt; 构造函数调用杀死了。有人能指出解决这个问题的最佳实践吗?

【问题讨论】:

  • 请问您为什么有builder 字段?我没有看到你使用它。 Java 已经具有协变返回类型,乍一看,我会说您可以删除这两个类型参数,因为它们似乎都是实现细节。不过这真的取决于用途......
  • 我有一个关于这个设置的快速问题@mr.nothing,你真的需要SimpleControl 是非抽象的吗?
  • 嗯,JavaFX 也是从构建器开始的,现在没有它们。所以建设者可能有风格上的缺陷。在您的情况下:更少的构造函数,控件类本身中的工厂方法(RadioButton.create().label("not me").build():
  • @JornVernee,哦,你说得对,这段代码有点多余。将编辑问题,谢谢。关于第二个参数(控件相关)——我应该如何实现构建方法来返回正确的控件类型?
  • @EpicPandaForce,是的,这就是控件的组织方式。

标签: java generics inheritance fluent-interface


【解决方案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 SimpleControlAutocompleteControl 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();
    }
}

【讨论】:

  • 我是否正确地认为没有优雅的方法可以解决这个问题?仍然会有可怕的带有类型参数的构造函数调用,可以逻辑上省略。
  • 好吧,我真的不知道这些参数的作用。如果您保留这些构造函数,您很可能需要在整个层次结构中保留 3 个构造函数,是的。我认为泛型已经大大简化了,我认为它非常优雅。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-07-22
  • 1970-01-01
  • 1970-01-01
  • 2020-08-07
  • 1970-01-01
  • 1970-01-01
  • 2019-08-20
相关资源
最近更新 更多