我有一个生成器类,该类从大多数方法中返回以允许进行菊花链连接。为了与子类一起使用,我希望父方法返回子实例,以便子方法可以链接到末尾。

public class BaseBuilder<T extends BaseBuilder<T>> {
    public T buildSomething() {
        doSomeWork();
        /* OPTION #1: */ return this;     // "Type mismatch: cannot convert from BaseBuilder<T> to T"
        /* OPTION #2: */ return T.this;   // "Type mismatch: cannot convert from BaseBuilder<T> to T"
        /* OPTION #3: */ return (T) this; // "Type safety: Unchecked cast from SqlBuilder<T> to T"
    }
}

public class ChildBuilder extends BaseBuilder<ChildBuilder> {}

选项#1和#2导致编译错误,选项#3导致警告(尽管可以使用@SuppressWarnings("unchecked")禁止显示)。这里有更好的方法吗?如何安全地将Basebuilder转换为Childbuilder?

最佳答案

声明ChildBuilder extends BaseBuilder<ChildBuilder>以某种方式指示代码气味,并且似乎违反了DRY。在此示例中,只能使用BaseBuilder参数化ChildBuilder,而不能使用其他参数设置参数,因此它应该是多余的。

我宁愿重新考虑是否真的要对此进行过度架构,并尝试将子代构建器中的所有方法放入BaseBuilder中。然后,我可以简单地从所有支持链接的方法中返回this

如果我仍然认为将特定的构建器方法组分成自己的类将受益,那么我将优先考虑组合,因为不建议仅将继承应用于代码重用。

假设我们有BaseBuilder的两个子类:

class BuilderA extends BaseBuilder<BuilderA> {
   BuilderA buildSomethingA() { return this; }
}

class BuilderB extends BaseBuilder<BuilderB> {
   BuilderB buildSomethingB() { return this; }
}

如果需要像这样将buildSomethingAbuildSomethingB链接起来怎么办:
builder.buildSomething().buildSomethingA().buildSomethingB();

如果不将子类方法移到BaseBuilder,我们将无法做到;但是想象一下还有BuilderC,这些方法没有意义,并且不应从BaseBuilder继承。

如果我们仍然将这两个方法移到父类(super class),然后再将另外三个方法移到父类(super class),那么我们将最终得到一个父类(super class),该类负责整个层次结构中90%的职责,并提供大量代码,例如:
if ((this instanceof BuilderB) && !flag1 && flag2) {
   ...
} else if ((this instanceof BuilderC) && flag1 && !flag2 && thing != null) {
   ...
} else if ...

我更喜欢的解决方案是DSL:
builder.buildSomething1().buildSomething2()
   .builderA()
      .buildSomethingA1().buildSomethingA2()
   .end()
   .buildSomething3()
   .builderB()
      .buildSomethingB()
   .end();

这里end()返回builder实例,因此您可以链接更多的方法或启动新的子生成器。

这样,(子)构建器可以继承其所需的内容(否则,它们只能扩展BaseBuilder),并可以具有自己有意义的层次结构或组成。

08-05 20:54