Java 生成器工厂返回不同的子接口

Java 生成器工厂返回不同的子接口,java,generics,factory,builder,Java,Generics,Factory,Builder,我知道关于堆栈溢出,这里有很多变体和相关主题,但我还没有找到任何令人信服的答案,所以我将亲自尝试一下 我正在尝试设计一个生成器工厂,它返回公共生成器接口的不同子类。我希望允许所有实现共享一个通用的抽象类,以便代码重用 请注意,我对build()方法的返回类型不感兴趣,只对构建器的类型感兴趣 这就是我到目前为止所做的: 具有子接口的通用生成器接口: interface FruitBuilder<T extends FruitBuilder<T>> { T taste

我知道关于堆栈溢出,这里有很多变体和相关主题,但我还没有找到任何令人信服的答案,所以我将亲自尝试一下

我正在尝试设计一个生成器工厂,它返回公共生成器接口的不同子类。我希望允许所有实现共享一个通用的抽象类,以便代码重用

请注意,我对
build()
方法的返回类型不感兴趣,只对构建器的类型感兴趣

这就是我到目前为止所做的:

具有子接口的通用生成器接口:

interface FruitBuilder<T extends FruitBuilder<T>> {
    T taste(String taste);
    T shape(String shape);
    T weight(String weight);

    Fruit build();
}
这些界面的用户应该能够像以下那样使用它:

 Fruit grapes = fruitBuilderFactory
    .grapes()
    .weight(4)
    .color("Purple")
    .clusterSize(4)  // Note that the GrapesBuilder type must be accessible here!
    .build();
大多数逻辑将进入抽象类,包括高级构建逻辑:

abstract class BaseFruitBuilder<T extends FruitBuilder<T>> implements FruitBuilder<T> {

   String taste;

   T taste(String taste) {
       this.taste = taste;
       return (T)this;     // Ugly cast!!!!!
   }

   ...

    Fruit build() {
       Fruit fruit = createSpecificInstance();

       // Do a lot of stuff on the fruit instance.

       return fruit;
    }

    protected abstract Fruit createSpecificInstance();
}
抽象类BaseFruitBuilder实现了FruitBuilder{
串味;
T味(弦味){
这个味道=味道;
返回(T)这个;//丑陋的演员阵容!!!!!
}
...
果肉{
水果=createSpecificInstance();
//在水果实例上做很多事情。
还果;
}
受保护的抽象实例();
}
考虑到基类,实现新的构建器非常简单:

interface FruitBuilderFactory {
    GrapesBuilder grapes();
    AppleBuilder apple();
    LemonBuilder lemon();
}
class GrapseBuilderImpl extends BaseFruitBuilder<GrapesBuilder> {
   int clusterSize;
   GrapesBuilder clusterSize(int clusterSize) {
       this.clusterSize = clusterSize;
   }

   protected Fruit createSpecificInstance() {
       return new Grape(clusterSize);
   }
}
类GrapseBuilderImpl扩展了BaseFruitBuilder{
集群规模;
GrapesBuilder clusterSize(int clusterSize){
this.clusterSize=clusterSize;
}
受保护水果createSpecificInstance(){
返回新葡萄(簇大小);
}
}

这是所有编译和罚款(至少我真正的代码)。问题是我是否可以删除抽象类中对T的丑陋转换。

您使用的是通常所指的,但似乎有点混淆了
T
所指的内容。您有一个
FruitBuilder
接口,该接口具有一个泛型类型
T
,它应该表示生成器将返回的类型。相反,您似乎在使用它来表示构建器本身的类型,这可能不是必需的(如果是,请参阅下面更复杂的建议)

小心使用泛型;它们是抽象概念这一事实使它们很容易混淆。在您的情况下,我建议使用以下界面:

interface FruitBuilder<F extends Fruit> {
  FruitBuilder<F> taste(...);
  FruitBuilder<F> shape(...);
  FruitBuilder<F> weight(...);
  F build();
}
对于大多数子类生成器模式,这就是您所需要的。即使需要为某些类型定义其他生成器方法,也可以使用此结构。考虑番石榴,和扩展它的子类型,并提供额外的方法。还请注意,它们将生成器直接与类型关联,而不是在公共生成器工厂类中。考虑自己复制该结构(例如:代码>葡萄。Builder < /C>,<代码>苹果。Builder < /Cuff>等)


在极少数情况下,您确实需要使用self类型,并将构建器表示为泛型类型。这创建了一个复杂的类型结构,但在实践中确实出现在某些地方,例如的(它的语义比大多数构建器要复杂得多)。注意,它有两个泛型
S
表示自身,而
T
表示实际使用的类型。如果您的
FruitBuilder
需要如此复杂,您可以使用类似的模式:

interface FruitBuilder<B extends FruitBuilder<B, F>, F> {
  B taste(...);
  B shape(...);
  B weight(...);
  F build();
}
界面生成器{ B味(…); B形(…); B重量(…); F build(); }
避免强制转换的一个选项是定义一个返回
T
的抽象方法:

abstract class BaseFruitBuilder<T extends FruitBuilder<T>> implements FruitBuilder<T> {

    String taste;

    T taste(String taste) {
       this.taste = taste;
       return returnThis();
    }

    protected abstract T returnThis();

     //...
}

class GrapseBuilderImpl extends BaseFruitBuilder<GrapesBuilder> {
    //...
    @Override
    protected T returnThis() {
        return this;
    }
}
抽象类BaseFruitBuilder实现了FruitBuilder{
串味;
T味(弦味){
这个味道=味道;
returnThis();
}
受保护的摘要T返回this();
//...
}
类GrapseBuilderImpl扩展了BaseFutureBuilder{
//...
@凌驾
受保护的T returnThis(){
归还这个;
}
}

缺点是您必须信任每个子类才能正确实现该方法。同样,在您的方法中,没有任何东西可以阻止任何人声明一个子类
GrapesBuilder扩展BaseFruitBuilder。我自己也用过这个,但从未意识到这是一个众所周知的成语

它不是很难看:)
T
打算成为
this
的类型,所以
(T)this
非常好。我宁愿使用
This
作为类型变量的名称,因此
(This)This
看起来更理智。然而,如果你真的讨厌演员阵容,有一个解决办法;但是我认为不值得这么做。如果所有的实现都共享相同的属性,为什么不能创建一个
BuilderParameters
类(具体的非泛型非最终类),然后将其输入到构建器中呢?客户端代码类似于
Fruit grapes=fruitBuilderFactory.grapes().build(GrapesBuilderParams.newInstance().weight(…).color(…).clusterSize(…)
。通过这样做,您将消除参数中的所有泛型(因此不再强制转换),并且在可定制性方面损失很少(即,构建器不能依赖于参数初始化顺序,因为他们不知道),也许这个技巧会起作用?Guava必须重写每个子类中的每个方法以返回当前类型。不是很优雅。@shmosel这是一个设计决定。由于实现的数量有限,而且构建器的目的是方便调用方使用,因此可以更清晰地隐藏实现中的不雅之处。使用self类型会将一些负担转嫁到打电话的人身上,在我看来,这远远不够优雅。例如,请注意构造的必要类型签名。这适用于Truth的用例,但使用构建器会使调用者的生活更加困难。自类型究竟是如何使调用者更加困难的?@shmosel如果需要将自类型实例分配给变量,调用者则必须正确声明该类型,这在最坏的情况下是棘手的,在最好的情况下只是额外的键入。这不是
IterableSubject
的问题,因为它的目的是
Grape grape = FruitBuilderFactory.grape()....build();
interface FruitBuilder<B extends FruitBuilder<B, F>, F> {
  B taste(...);
  B shape(...);
  B weight(...);
  F build();
}
abstract class BaseFruitBuilder<T extends FruitBuilder<T>> implements FruitBuilder<T> {

    String taste;

    T taste(String taste) {
       this.taste = taste;
       return returnThis();
    }

    protected abstract T returnThis();

     //...
}

class GrapseBuilderImpl extends BaseFruitBuilder<GrapesBuilder> {
    //...
    @Override
    protected T returnThis() {
        return this;
    }
}