【发布时间】:2016-04-25 07:23:39
【问题描述】:
我想在我即将进行的一些工作中使用构建器模式,该工作在层次结构中有多个类。基类将至少有 9 个字段开始,各个子类可能会添加 2-4 个 more 字段。这很快就会失控,而建造者模式正是出于这个原因对我很有吸引力。我在书籍和文章中初步了解了构建器模式。他们很有帮助,但对如何扩展这种模式一无所知。我试图自己实现这一点,但是我遇到了每个子类的构造函数的问题,因为我不知道如何将构建器中收集的数据传递给超类。我在 SO 上寻找一些答案,这就是我发现的。
这个来自SO 24243240,其中给出了如何使用抽象构建器扩展抽象类的示例。也是基于这个blog post。
public abstract class AbstractA {
protected String s;
protected int i;
protected AbstractA() {
}
protected abstract static class ABuilder<T extends AbstractA, B extends ABuilder<T,B>> {
protected T object;
protected B thisObject;
protected abstract T getObject(); //Each concrete implementing subclass overrides this so that T becomes an object of the concrete subclass
protected abstract B thisObject(); //Each concrete implementing subclass builder overrides this for the same reason, but for B for the builder
protected ABuilder() {
object = getObject();
thisObject = thisObject();
}
public B withS(String s) {
object.s = s;
return thisObject;
}
public B withI(int i) {
object.i = i;
return thisObject;
}
public T build() {
return object;
}
}
}
public final class ConcreteA extends AbstractA {
private String foo;
protected ConcreteA() {
}
public static final class Builder extends AbstractA.ABuilder<ConcreteA,Builder> {
@Override protected ConcreteA getObject() {
return new ConcreteA();
}
@Override protected Builder thisObject() {
return this;
}
public Builder() {
}
public Builder withFoo(String foo) {
object.foo = foo;
return this;
}
}
}
然后在客户端代码中,它看起来像......
ConcreteA baz = new ConcreteA.Builder().withFoo("foo").withS("bar").withI(0).build();
我喜欢这个示例,因为它允许您轻松地扩展这些类,但在我看来,这也违背了使用构建器模式的目的,因为方法 withS(String s) 和 withI(int i) 的行为很像 setter 方法。此外,此方法将基类和构建器类的字段保留为受保护而不是私有。
这是来自SO 17164375的一封邮件
public class NutritionFacts {
private final int calories;
public static class Builder<T extends Builder> {
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;
}
}
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;
}
}
我喜欢这个似乎更接近 Josh Bloch 描述的构建器模式,它还允许您简单地将构建器传递给您想要实例化的类的构造器。这将是在构建器中进行一些验证的好方法在实例化调用build() 中的对象。同时,这个例子展示了如何用具体类扩展构建器模式,当你这样做时,扩展具体类所带来的所有讨厌的可能性(例如不一致的接口,可能破坏状态的继承方法)你的对象等)
所以我的问题是有没有办法用抽象构建器实现抽象类,它还允许您在基类的构造器中传递对构建器的引用?比如:
public abstract BaseClass {
// various fields go here
...
public abstract Builder<T extends BaseClass, B extends Builder<T,B>> {
// add chaining methods here
...
public T build() {
if (isValid()) return new T(this);
else Throw new IllegalArgumentException("Invalid data passed to builder.");
}
}
public BaseClass(Builder builder) {
// set fields of baseclass here
}
}
我意识到你不能像我在这里展示的那样实例化一个对象,但是我的意思是还有其他方法吗?这可能是工厂去的地方吗?也许我只是对一般的构建器模式有错误的假设。 :) 如果是这样的话,有没有更好的方向?
【问题讨论】:
标签: java generics inheritance design-patterns builder