简体   繁体   English

使用抽象生成器vs具体类和具体生成器扩展抽象类

[英]Extending an abstract class with an abstract builder vs concrete class and concrete builder

I would like to use the builder pattern in some upcoming work that I have which has several classes in a hierarchy. 我想在即将进行的一些工作中使用构建器模式,该工作在层次结构中具有多个类。 The base class will have at least 9 fields to start, and the various sub-classes may add between 2-4 more fields each. 基类将至少有9个字段开始,各个子类可能会分别增加2-4 字段。 This would get out of hand very quickly and the builder pattern is appealing to me for this exact reason. 这将很快失去控制,并且出于这个确切的原因,构建器模式吸引了我。 I got some initial exposure to the builder pattern in books and articles. 我初步了解了书籍和文章中的构建器模式。 They were helpful, but had nothing on how to extend this pattern. 他们很有帮助,但是对如何扩展这种模式一无所知。 I tried to implement this by myself, but I ran into trouble with the constructors of each of the sub-classes because I didn't get how to pass the collected data in the builder to super class. 我试图自己实现它,但是我遇到了每个子类的构造函数的麻烦,因为我不知道如何将生成器中收集的数据传递给超类。 I looked on SO for some answers, and here's what I found. 我在SO上寻找了一些答案,这就是我所发现的。

This one is from SO 24243240 where an example of how to extend an abstract class with an abstract builder is given. 这是来自SO 24243240的示例,其中给出了如何使用抽象生成器扩展抽象类的示例。 It is also based on this 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;
        }
    }
}

And then in client code, it would look like... 然后在客户端代码中,它看起来像...

ConcreteA baz = new ConcreteA.Builder().withFoo("foo").withS("bar").withI(0).build();

I like this example because it allows you to easily extend these classes, but it also seems to me that this defeats the purpose of using the builder pattern because the methods withS(String s) and withI(int i) act alot like setter methods. 我喜欢这个示例,因为它允许您轻松扩展这些类,但是在我看来,这也withS(String s)了使用构建器模式的目的,因为withS(String s)withI(int i)方法的作用与setter方法类似。 Also, this method leaves the fields of the base class and the builder class as protected rather than private. 同样,此方法将基类和构建器类的字段保留为受保护的,而不是私有的。

Here's one from SO 17164375 这是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;
    }
}

I like that this one seemingly adheres more closely to the builder pattern described by Josh Bloch and it also allows you to simply pass the builder into the constructor for the class you want to instantiate. 我喜欢这一点似乎更符合Josh Bloch所描述的构建器模式,它还允许您将构建器简单地传递给要实例化的类的构造器。 This would be a nice way to do some validation inside the builder before instantiating the object in the call to build() . 实例化build()调用中的对象之前,这是在构建器内部进行验证的一种好方法。 At the same time though, this example shows how you can extend the builder pattern with concrete classes, and when you do that the potential for all the nastiness that comes with extending concrete classes (eg inconsistent interfaces, inheriting methods which can corrupt the state of your object, etc.) 但是,同时,此示例显示了如何使用具体的类扩展构建器模式,以及当您这样做时,扩展具体类所带来的所有麻烦(例如,不一致的接口,继承会破坏状态的方法)的可能性。您的对象等)

So my question is there a way to implement an abstract class with an abstract builder that also allows you to pass in a reference to a builder in the constructor for the base class? 所以我的问题是,有没有一种方法可以用抽象构建器实现抽象类,还允许您将对基类的构造器的引用传递给构建器? Something like: 就像是:

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
}
}

I realize that you can't instantiate an object the way that I've shown here, but is there some other way to do it I mean? 我意识到您不能像在此所示的那样实例化一个对象,但是还有其他方法可以实现吗? Is this possibly where a factory would go? 这可能是工厂要去的地方吗? Maybe I just have the wrong assumptions about the builder pattern in general. 也许我只是对构建器模式有一个错误的假设。 :) If that's the case, is there a better direction to take? :)如果是这样,是否有更好的方向?

Your first example is not bad, but I don't think it is what you are looking for. 您的第一个例子还不错,但我认为这不是您要寻找的。

I am still a little unsure of exactly what you want, but seeing your examples do not work for you, I thought I'd give you one or two of my own. 我仍然不确定您到底想要什么,但是看到您的示例对您不起作用,我想我会给自己一个或两个。 :) :)

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;
    }
}

Any new type, would have its own builder, taking in its parent's builder. 任何新类型都将拥有其自己的构建器,并采用其父代的构建器。 As you can see this is similar to: 如您所见,这类似于:

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

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

In your example. 在您的示例中。

This however, quickly gets out of hand as well, increasingly for the number of variables and subclasses. 但是,随着变量和子类的数量增加,这也很快变得不可收拾。

An alternativ, would be to use dynanic variables, have a look at this: http://martinfowler.com/apsupp/properties.pdf Martin Fowler writes a great article specifying all the pros and cons. 另一种方法是使用动态变量,请看一下: http ://martinfowler.com/apsupp/properties.pdf Martin Fowler撰写了一篇很棒的文章,详细说明了所有优点和缺点。

Anyways, here's my second example: 无论如何,这是我的第二个示例:

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;
    }
}

The magic here is, you (might) no longer need all these subclasses. 这里的魔术是,您(可能)不再需要所有这些子类。 If these subclasses' behavior does not change, but only their variables, you should be fine using a system like this. 如果这些子类的行为仅改变了它们的变量,则没有变化,那么使用这样的系统应该没问题。 I strongly advise that you read Martin Fowler article on the subject though, there are good places and bad places to do this, but I think this is a good one. 我强烈建议您阅读有关该主题的Martin Fowler文章,尽管这样做有很多好地方,但我认为这是个好地方。

I hope this brings you closer to an answer, good luck. 我希望这使您更接近答案,祝您好运。 :) :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM