使用抽象构建器与具体类和具体构建器

时间:2016-01-19 17:27:35

标签: java generics inheritance design-patterns builder

我想在我即将开展的一些工作中使用构建器模式,它在层次结构中有几个类。基类将至少有9个字段可以启动,而各个子类可以在每个字段之间添加2-4个更多字段。由于这个确切的原因,这将很快失控,建造者模式对我很有吸引力。我在书籍和文章中初步了解了构建器模式。他们很有帮助,但对如何扩展这种模式一无所知。我试图自己实现这个,但是我遇到了每个子类的构造函数的问题,因为我没有得到如何将构建器中收集的数据传递给超类。我在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)很像二传手术方法。此外,此方法将基类和构建器类的字段保留为受保护而不是私有。

此处来自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
}
}

我意识到你可以按照我在这里展示的方式实例化一个对象,但我还有其他方法吗?这可能是工厂要去的地方吗?也许我只是对构建器模式有错误的假设。 :)如果是这样的话,还有更好的方向吗?

1 个答案:

答案 0 :(得分:0)

你的第一个例子并不差,但我认为这不是你想要的。

我仍然不确定你想要什么,但看到你的例子不适合你,我想我会给你一两个我自己的。 :)

class ParentBuilder{
    public ConcreteParent build(){
        ConcreteParent parent = new ConcreteParent();
        parent.setFirst(1);
        parent.setSecond(2);
        parent.setThird(3);
        return parent;
    }
}

class ChildBuilder{
    public ConcreteChild build(ParentBuilder parentBuilder){
        ConcreteParent parent = parentBuilder.build();
        ConcreteChild child = new ConcreteChild();
        child.setFirst(parent.getFirst());
        child.setSecond(parent.getSecond());
        child.setThird(parent.getThird());
        child.setFourth(4); //Child specific value
        child.setFifth(5); //Child specific value
        return child;
    }
}

任何新类型都有自己的构建器,接收其父构建器。 如您所见,这类似于:

        public NutritionFacts build() { return new NutritionFacts(this); }
    }

    protected NutritionFacts(Builder builder) {
        calories = builder.calories;
    }

在你的例子中。

然而,对于变量和子类的数量,这种情况很快就会失控。

一个替代方案,就是使用动态变量,看看这个:http://martinfowler.com/apsupp/properties.pdf Martin Fowler写了一篇很好的文章,详细说明了所有的优点和缺点。

无论如何,这是我的第二个例子:

public class Demo {

    public static void main(String[] args) {
        ConcreteBuilder builder = new ConcreteBuilder();
        Concrete concrete = builder.with("fourth", "valueOfFourth").build();
        for(String value : concrete.getAttributes().values())
            System.out.println(value);
    }
}

class ConcreteBuilder{
    private Concrete concrete;

    public ConcreteBuilder(){
        concrete = new Concrete();
    }

    public ConcreteBuilder with(String key, String value){
        concrete.getAttributes().put(key, value);
        return this;
    }
    public Concrete build(){
        return concrete;
    }
}

class Concrete{
    private HashMap<String, String> attributes;

    public Concrete(){
        attributes = new HashMap<>();
    }

    public HashMap<String, String> getAttributes(){
        attributes.put("first", "valueOfFirst");
        attributes.put("second", "valueOfSecond");
        attributes.put("third", "valueOfThird");
        return attributes;
    }
}

这里的魔力是,您(可能)不再需要所有这些子类。 如果这些子类&#39;行为不会改变,但只有他们的变量,你应该使用这样的系统。 我强烈建议你阅读Martin Fowler关于这个主题的文章,虽然有很好的地方和不好的地方,但我认为这是一个很好的。

我希望这会让你更接近答案,祝你好运。 :)