Builder Factory 返回不同的子接口
Builder Factory returning different sub-interfaces
我知道这里有很多关于堆栈溢出的变体和相关主题,但我没有找到任何令人信服的答案,所以我会自己试一试。
我正在尝试设计一个构建器工厂,它 return 具有通用构建器界面的不同子class。我想允许所有实现共享一个公共抽象 class 以供代码重用。
请注意,我对 build()
方法的 return 类型不感兴趣,只对构建器的类型感兴趣。
这是我目前拥有的:
具有通用子接口的生成器接口:
interface FruitBuilder<T extends FruitBuilder<T>> {
T taste(String taste);
T shape(String shape);
T weight(String weight);
Fruit build();
}
一些构建器有额外的方法:
interface GrapesBuilder extends FruitBuilder<GrapeBuilder> {
GrapesBuilder clusterSize(int clusterSize);
}
接下来是指定return具体建造者的工厂:
interface FruitBuilderFactory {
GrapesBuilder grapes();
AppleBuilder apple();
LemonBuilder lemon();
}
这些界面的用户应该能够像这样使用它:
Fruit grapes = fruitBuilderFactory
.grapes()
.weight(4)
.color("Purple")
.clusterSize(4) // Note that the GrapesBuilder type must be accessible here!
.build();
大部分逻辑将进入抽象 class,包括高级构建逻辑:
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();
}
鉴于基础 class,实现新构建器真的很简单:
class GrapseBuilderImpl extends BaseFruitBuilder<GrapesBuilder> {
int clusterSize;
GrapesBuilder clusterSize(int clusterSize) {
this.clusterSize = clusterSize;
}
protected Fruit createSpecificInstance() {
return new Grape(clusterSize);
}
}
这一切都在编译并且很好(至少是我的真实代码)。问题是我是否可以在摘要 class.
中删除对 T 的丑陋转换
您使用的是通常所说的 self-types,但似乎有点混淆了 T
所指的内容。您有一个具有通用类型 T
的 FruitBuilder
接口, 应该 表示构建器将 return 的类型。相反,您似乎在使用它来表示构建器本身的类型,这可能不是必需的(如果是,请参阅下面的更复杂的建议)。
小心谨慎地使用泛型;它们是抽象概念这一事实使它们很容易混淆。在您的情况下,我建议使用以下界面:
interface FruitBuilder<F extends Fruit> {
FruitBuilder<F> taste(...);
FruitBuilder<F> shape(...);
FruitBuilder<F> weight(...);
F build();
}
个人建造者现在声明他们最终将建造的类型,而不是他们自己的类型:
interface FruitBuilderFactory {
GrapesBuilder grapes(); // define a concrete subtype to add methods
FruitBuilder<Apple> apple();
}
现在每个 FruitBuilder
显然都是 F
个实例的构建器,并且您的每个构建器方法都可以干净地 return this
和您的 build()
方法将 return 一个预期泛型类型的对象,让你可以这样写:
Grape grape = FruitBuilderFactory.grape()....build();
对于大多数子class 构建器模式,这就是您所需要的。即使您需要为某些类型定义额外的构建器方法,您仍然可以使用此结构。考虑扩展它并提供附加方法的 Guava 的 ImmutableCollection.Builder<E>
, and the ImmutableMultiset.Builder<E>
子类型。另请注意,它们将构建器直接与类型相关联,而不是在公共构建器工厂 class 中。考虑自己复制该结构(例如 Grape.Builder
、Apple.Builder
等)。
在极少数情况下,您确实需要使用自身类型并将构建器也表示为通用类型。这创建了一个复杂的类型结构,但在实践中确实出现在某些地方,例如 Truth's Subject<S extends Subject<S,T>,T>
(它的语义比大多数构建器复杂得多)。注意它有两个泛型; S
代表它自己,而 T
代表实际使用的类型。如果您的 FruitBuilder
需要如此复杂,您可以使用类似的模式:
interface FruitBuilder<B extends FruitBuilder<B, F>, F> {
B taste(...);
B shape(...);
B weight(...);
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;
}
}
缺点是您必须信任每个子类才能正确实现该方法。再一次,用你的方法,没有什么能阻止任何人声明子类GrapesBuilder extends BaseFruitBuilder<AppleBuilder>
,所以你需要在某种程度上信任子类。
编辑 刚刚意识到这个解决方案被@user158037 的 引用了。我自己用过这个,但从来没有意识到这是一个众所周知的习语。 :-)
我知道这里有很多关于堆栈溢出的变体和相关主题,但我没有找到任何令人信服的答案,所以我会自己试一试。
我正在尝试设计一个构建器工厂,它 return 具有通用构建器界面的不同子class。我想允许所有实现共享一个公共抽象 class 以供代码重用。
请注意,我对 build()
方法的 return 类型不感兴趣,只对构建器的类型感兴趣。
这是我目前拥有的:
具有通用子接口的生成器接口:
interface FruitBuilder<T extends FruitBuilder<T>> {
T taste(String taste);
T shape(String shape);
T weight(String weight);
Fruit build();
}
一些构建器有额外的方法:
interface GrapesBuilder extends FruitBuilder<GrapeBuilder> {
GrapesBuilder clusterSize(int clusterSize);
}
接下来是指定return具体建造者的工厂:
interface FruitBuilderFactory {
GrapesBuilder grapes();
AppleBuilder apple();
LemonBuilder lemon();
}
这些界面的用户应该能够像这样使用它:
Fruit grapes = fruitBuilderFactory
.grapes()
.weight(4)
.color("Purple")
.clusterSize(4) // Note that the GrapesBuilder type must be accessible here!
.build();
大部分逻辑将进入抽象 class,包括高级构建逻辑:
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();
}
鉴于基础 class,实现新构建器真的很简单:
class GrapseBuilderImpl extends BaseFruitBuilder<GrapesBuilder> {
int clusterSize;
GrapesBuilder clusterSize(int clusterSize) {
this.clusterSize = clusterSize;
}
protected Fruit createSpecificInstance() {
return new Grape(clusterSize);
}
}
这一切都在编译并且很好(至少是我的真实代码)。问题是我是否可以在摘要 class.
中删除对 T 的丑陋转换您使用的是通常所说的 self-types,但似乎有点混淆了 T
所指的内容。您有一个具有通用类型 T
的 FruitBuilder
接口, 应该 表示构建器将 return 的类型。相反,您似乎在使用它来表示构建器本身的类型,这可能不是必需的(如果是,请参阅下面的更复杂的建议)。
小心谨慎地使用泛型;它们是抽象概念这一事实使它们很容易混淆。在您的情况下,我建议使用以下界面:
interface FruitBuilder<F extends Fruit> {
FruitBuilder<F> taste(...);
FruitBuilder<F> shape(...);
FruitBuilder<F> weight(...);
F build();
}
个人建造者现在声明他们最终将建造的类型,而不是他们自己的类型:
interface FruitBuilderFactory {
GrapesBuilder grapes(); // define a concrete subtype to add methods
FruitBuilder<Apple> apple();
}
现在每个 FruitBuilder
显然都是 F
个实例的构建器,并且您的每个构建器方法都可以干净地 return this
和您的 build()
方法将 return 一个预期泛型类型的对象,让你可以这样写:
Grape grape = FruitBuilderFactory.grape()....build();
对于大多数子class 构建器模式,这就是您所需要的。即使您需要为某些类型定义额外的构建器方法,您仍然可以使用此结构。考虑扩展它并提供附加方法的 Guava 的 ImmutableCollection.Builder<E>
, and the ImmutableMultiset.Builder<E>
子类型。另请注意,它们将构建器直接与类型相关联,而不是在公共构建器工厂 class 中。考虑自己复制该结构(例如 Grape.Builder
、Apple.Builder
等)。
在极少数情况下,您确实需要使用自身类型并将构建器也表示为通用类型。这创建了一个复杂的类型结构,但在实践中确实出现在某些地方,例如 Truth's Subject<S extends Subject<S,T>,T>
(它的语义比大多数构建器复杂得多)。注意它有两个泛型; S
代表它自己,而 T
代表实际使用的类型。如果您的 FruitBuilder
需要如此复杂,您可以使用类似的模式:
interface FruitBuilder<B extends FruitBuilder<B, F>, F> {
B taste(...);
B shape(...);
B weight(...);
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;
}
}
缺点是您必须信任每个子类才能正确实现该方法。再一次,用你的方法,没有什么能阻止任何人声明子类GrapesBuilder extends BaseFruitBuilder<AppleBuilder>
,所以你需要在某种程度上信任子类。
编辑 刚刚意识到这个解决方案被@user158037 的