当前位置: 首页 > 知识库问答 >
问题:

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

廉博赡
2023-03-14

我想在我即将完成的一些工作中使用builder模式,它在一个层次结构中有几个类。基类将至少有9个字段要启动,各个子类可能会添加2-4个字段。这会很快失控,而builder模式正是出于这个原因吸引了我。我在书籍和文章中初步接触了builder模式。他们是有帮助的,但没有关于如何扩展这种模式。我试图自己实现这一点,但是我在每个子类的构造函数中遇到了麻烦,因为我不知道如何将构建器中收集的数据传递给超级类。我一直在寻找答案,这是我的发现。

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();

我喜欢这个示例,因为它允许您轻松地扩展这些类,但在我看来,这也违背了使用builder模式的目的,因为withs(Srings)withi(int I)方法的作用很像setter方法。此外,此方法将基类和生成器类的字段保留为protected而不是private。

这是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个答案

奚和光
2023-03-14

你的第一个例子还不错,但我认为这不是你要找的。

我仍然有点不确定你到底想要什么,但看到你的例子对你不起作用,我想我给你一两个我自己的。:)

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

这里的神奇之处在于,您(可能)不再需要所有这些子类。如果这些子类的行为没有改变,而只是它们的变量,那么使用这样的系统就可以了。我强烈建议你阅读马丁福勒的文章,虽然,有好的地方和坏的地方这样做,但我认为这是一个好的。

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

 类似资料:
  • 问题内容: 我之前了解到抽象类可以扩展具体类。尽管我没从JAVA设计师那里看到原因,但是没关系。我还了解到,扩展具体类的抽象类可以使重写的方法抽象。为什么?您可以提供有用的用例吗?我正在尝试学习设计模式,我不想错过任何东西。 这是示例: 问题答案: 如果我有一组想要默认实现的类(以便它们可以从扩展),并且想要强制提供自己的实现(在这种情况下使其抽象为子类会强制执行此操作。) 当然,此示例中的另一种

  • 给出了什么: 我使用Hibernate代码生成生成我的模型类(Hibernate工具 4) 我想要为每个数据库表提供一个抽象基类,以及一个具体的扩展 基类应包含所有数据库字段(获取/设置) 具体类应从基层开始延伸,对于跨站字段应为空 我用阿帕奇·马文处理我的图书馆 我有一个爪哇配置的SpringMVC环境 我听说您可以覆盖Hibernate工具JAR中的Freemarker模板。但是当我使用Mav

  • 当我们把类看作一种数据类型时,我们通常认定该类型的对象是要被实例化的。但是,在许多情况下,定义不实例化为任何对象的类是很有用处的,这种类称为 抽象类 (abstract class)。因为抽象类要作为基类被其他类继承,所以通常也把它称为 抽象基类(abstract base class)。抽象基类不能用来建立实例化的对象。 抽象类的惟一用途是为其他类提供合适的基类,其他类可从它这里继承和(或)实现

  • 我知道抽象类可能包含抽象和具体方法(即主体实现)。我的问题是:子类可以从抽象超类继承/覆盖具体方法吗?其次,它们必须以实现抽象方法的方式实现具体方法吗?

  • 如何让“教授”延伸到“人”?我应该直接在代码上做吗?还是我错过了什么?

  • 问题内容: 当其中的所有方法都是具体的时,有人在声明类抽象时有一些实际的编程情况吗? 问题答案: 好吧,您可能正在使用模板方法模式,其中存在多个都具有默认实现的覆盖点,但是组合的默认实现本身不合法-任何功能实现都必须是子类。 (是的,我不喜欢模板方法模式;)