Ken*_*Y-N 123 java design-patterns
给这篇Dr Dobbs文章,特别是Builder Pattern,我们如何处理子类化Builder的情况?以我们想要子类化添加GMO标签的示例的简化版本为例,一个简单的实现将是:
public class NutritionFacts {
private final int calories;
public static class Builder {
private int calories = 0;
public Builder() {}
public Builder calories(int val) { calories = val; return this; }
public NutritionFacts build() { return new NutritionFacts(this); }
}
protected NutritionFacts(Builder builder) {
calories = builder.calories;
}
}
Run Code Online (Sandbox Code Playgroud)
子类:
public class GMOFacts extends NutritionFacts {
private final boolean hasGMO;
public static class Builder extends NutritionFacts.Builder {
private boolean hasGMO = false;
public Builder() {}
public Builder GMO(boolean val) { hasGMO = val; return this; }
public GMOFacts build() { return new GMOFacts(this); }
}
protected GMOFacts(Builder builder) {
super(builder);
hasGMO = builder.hasGMO;
}
}
Run Code Online (Sandbox Code Playgroud)
现在,我们可以编写如下代码:
GMOFacts.Builder b = new GMOFacts.Builder();
b.GMO(true).calories(100);
Run Code Online (Sandbox Code Playgroud)
但是,如果我们得到错误的订单,那一切都会失败:
GMOFacts.Builder b = new GMOFacts.Builder();
b.calories(100).GMO(true);
Run Code Online (Sandbox Code Playgroud)
问题当然是NutritionFacts.Builder返回a NutritionFacts.Builder而不是a GMOFacts.Builder,所以我们如何解决这个问题,还是有更好的模式可以使用?
注意:这个类似问题的答案提供了我上面的课程; 我的问题是关于确保构建器调用的顺序正确的问题.
gka*_*mal 158
您可以使用泛型来解决它.我认为这被称为"奇怪的反复出现的通用模式"
使基类构建器方法的返回类型成为通用参数.
public class NutritionFacts {
private final int calories;
public static class Builder<T extends Builder<T>> {
private int calories = 0;
public Builder() {}
public T calories(int val) {
calories = val;
return (T) this;
}
public NutritionFacts build() { return new NutritionFacts(this); }
}
protected NutritionFacts(Builder<?> builder) {
calories = builder.calories;
}
}
Run Code Online (Sandbox Code Playgroud)
现在使用派生类构建器作为泛型参数来实例化基础构建器.
public class GMOFacts extends NutritionFacts {
private final boolean hasGMO;
public static class Builder extends NutritionFacts.Builder<Builder> {
private boolean hasGMO = false;
public Builder() {}
public Builder GMO(boolean val) {
hasGMO = val;
return this;
}
public GMOFacts build() { return new GMOFacts(this); }
}
protected GMOFacts(Builder builder) {
super(builder);
hasGMO = builder.hasGMO;
}
}
Run Code Online (Sandbox Code Playgroud)
Ste*_*vra 35
只是为了记录,摆脱了
unchecked or unsafe operations警告
对于return (T) this;声明为@dimadima和@Thomas N.谈论,以下解决方案适用于某些情况.
使abstract构造器声明泛型类型(T extends Builder在本例中)并声明protected abstract T getThis()抽象方法如下:
public abstract static class Builder<T extends Builder<T>> {
private int calories = 0;
public Builder() {}
/** The solution for the unchecked cast warning. */
public abstract T getThis();
public T calories(int val) {
calories = val;
// no cast needed
return getThis();
}
public NutritionFacts build() { return new NutritionFacts(this); }
}
Run Code Online (Sandbox Code Playgroud)
有关详细信息,请参阅http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ205.
Q23*_*Q23 19
基于博客文章,这种方法要求所有非叶类都是抽象的,并且所有叶类必须是最终的.
public abstract class TopLevel {
protected int foo;
protected TopLevel() {
}
protected static abstract class Builder
<T extends TopLevel, B extends Builder<T, B>> {
protected T object;
protected B thisObject;
protected abstract T createObject();
protected abstract B thisObject();
public Builder() {
object = createObject();
thisObject = thisObject();
}
public B foo(int foo) {
object.foo = foo;
return thisObject;
}
public T build() {
return object;
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后,您有一些扩展此类及其构建器的中间类,以及您需要的更多内容:
public abstract class SecondLevel extends TopLevel {
protected int bar;
protected static abstract class Builder
<T extends SecondLevel, B extends Builder<T, B>> extends TopLevel.Builder<T, B> {
public B bar(int bar) {
object.bar = bar;
return thisObject;
}
}
}
Run Code Online (Sandbox Code Playgroud)
最后,一个具体的叶类,可以按任何顺序调用任何父类的所有构建器方法:
public final class LeafClass extends SecondLevel {
private int baz;
public static final class Builder extends SecondLevel.Builder<LeafClass,Builder> {
protected LeafClass createObject() {
return new LeafClass();
}
protected Builder thisObject() {
return this;
}
public Builder baz(int baz) {
object.baz = baz;
return thisObject;
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以从层次结构中的任何类调用任何顺序的方法:
public class Demo {
LeafClass leaf = new LeafClass.Builder().baz(2).foo(1).bar(3).build();
}
Run Code Online (Sandbox Code Playgroud)
您也可以覆盖该calories()方法,并让它返回扩展构建器.这是因为Java支持协变返回类型.
public class GMOFacts extends NutritionFacts {
private final boolean hasGMO;
public static class Builder extends NutritionFacts.Builder {
private boolean hasGMO = false;
public Builder() {
}
public Builder GMO(boolean val)
{ hasGMO = val; return this; }
public Builder calories(int val)
{ super.calories(val); return this; }
public GMOFacts build() {
return new GMOFacts(this);
}
}
[...]
}
Run Code Online (Sandbox Code Playgroud)