用于具体实现抽象类的构建器(Joshua Bloch 风格)?

2024-01-19

假设我有一个抽象类(BaseThing)。它有一个必需参数(“基本必需”)和一个可选参数(“基本可选”)。我有一个扩展它的具体类(Thing)。它还具有一个必需参数(“必需”)和一个可选参数(“可选”)。所以像这样:

public abstract class BaseThing {
    public static final String DEFAULT_BASE_OPTIONAL = "Default Base Optional";

    private final String baseRequired;
    private String baseOptional = DEFAULT_BASE_OPTIONAL;

    protected BaseThing(final String theBaseRequired) {
        this.baseRequired = theBaseRequired;
    }

    final void setBaseOptional(final String newVal) {
        this.baseOptional = newVal;
    }

    public final void selfDescribe() {
        System.out.println("Base Required: " + baseRequired);
        System.out.println("Base Optional: " + baseOptional);

        selfDescribeHook();
    }

    protected abstract void selfDescribeHook();
}

and:

public final class Thing extends BaseThing {
    public static final String DEFAULT_OPTIONAL = "Default Optional";

private final String required;
    private String optional = DEFAULT_OPTIONAL;

    Thing(final String theRequired, final String theBaseRequired) {
        super(theBaseRequired);
        required = theRequired;
    }

    @Override
    protected void selfDescribeHook() {
        System.out.println("Required: " + required);
        System.out.println("Optional: " + optional);
    }

    void setOptional(final String newVal) {
        optional = newVal;
    }
}

我想要一个 Joshua Bloch 风格的 Thing 对象构建器。不过,更一般地说,我想让 BaseThing 的具体实现更容易拥有构建器,所以我真正想要的(我认为)是一个可以轻松用于制作 ThingBuilder、OtherThingBuilder 或 SuperThingBuilder 的 BaseThing 构建器。

有没有比我想出的以下方法更好的方法(或者我想出的方法有问题)?

public abstract class BaseThingBuilder<T extends BaseThing> {
    private String baseOptional = BaseThing.DEFAULT_BASE_OPTIONAL;

    public BaseThingBuilder<T> setBaseOptional(final String value) {
        baseOptional = value;
        return this;
    }

    public T build() {
        T t = buildHook();
        t.setBaseOptional(baseOptional);

        return t;
    }

    protected abstract T buildHook();
}

and:

public final class ThingBuilder extends BaseThingBuilder<Thing> {
    private final String baseRequired;
    private final String required;
    private String optional = Thing.DEFAULT_OPTIONAL;

    public ThingBuilder(final String theRequired,
            final String theBaseRequired) {
        required = theRequired;
        baseRequired = theBaseRequired;
    }

    public ThingBuilder setOptional(final String value) {
        optional = value;
        return this;
    }

    protected Thing buildHook() {
        Thing thing = new Thing(required, baseRequired);
        thing.setOptional(optional);

        return thing;
    }
}

它可用于以类似于以下方式构建 Thing 对象:

        BaseThingBuilder<Thing> builder = 
                new ThingBuilder("Required!", "Base Required!")
                    .setOptional("Optional!")
                    .setBaseOptional("Base Optional!");
        Thing thing = builder.build();
        thing.selfDescribe();

哪个输出:

Base Required: Base Required!
Base Optional: Base Optional!
Required: Required!
Optional: Optional!

我知道但我不认为特别重要的一个问题是,在设置任何基本选项之前,您必须设置所有非基本选项:否则会导致语法错误,因为 setBaseOptional() 返回 BaseThingBuilder 而不是 ThingBuilder。

提前致谢。


我认为以这种方式思考建设者不是一个好主意。构建者的层次结构通常会导致令人头痛和脆弱的代码。

减少需要在具体构建器中编写的代码量并重用基础构建器中的逻辑与领域密切相关。开发通用解决方案并不容易。但是,无论如何,让我们尝试看一个例子:

public interface Builder<T> {
  T build();
}

public class Person {
  private final String name;

  //the proper way to use a builder is to pass an instance of one to
  //the class that is created using it...
  Person(PersonBuilder builder) {
    this.name = builder.name;
  }

  public String getName(){ return name; }

  public static class PersonBuilder implements Builder<Person> {
    private String name;
    public PersonBuilder name(String name){ this.name = name; return this; }

    public Person build() {
      if(name == null) {
        throw new IllegalArgumentException("Name must be specified");
      }
      return new Person(this);
    }
  }
}

棒极了,宝贝!怎么办?也许您想添加一个班级来代表学生。你做什么工作?你扩展Person吗?当然,这是有效的。采取更“奇怪”的路线并尝试聚合怎么样?是的,您也可以这样做...您的选择将影响您最终如何实现构建器。假设您坚持传统路径并扩展 Person(您应该已经开始问自己,这对于Person成为一个具体的类?如果我把它变得抽象,我真的需要一个构建器吗?如果类是抽象的,那么构建器应该是抽象的吗?):

public class Student extends Person {
  private final long id;

  Student(StudentBulder builder) {
    super(builder);
    this.id = builder.id;
  }

  public long getId(){ return id; }

  //no need for generics, this will work:
  public static class StudentBuilder extends PersonBuilder {
    private long id;
    public StudentBuilder id(long id){ this.id = id; return this; }

    public Student build() {
      if(id <= 0) {
        throw new IllegalArgumentException("ID must be specified");
      }
      return new Student(this);
    }
  }
}

好的,这看起来正是你想要的!那么,你尝试一下:

Person p = new PersonBuilder().name("John Doe").build();
Student s = new StudentBuilder().name("Jane Doe").id(165).build();

看起来很棒!除此之外,它无法编译...第 2 行有一个错误,它指出The method id(int) is undefined for the type Person.PersonBuilder。问题是PersonBuilder#name返回类型的构建器PersonBuilder,这不是你想要的。在StudentBuilder你实际上想要的返回类型name to be StudentBuilder。现在,你提前思考并意识到如果有任何事情延伸StudentBuilder你希望它完全返回其他东西......这可行吗?是的,使用泛型。然而,它非常丑陋并且引入了相当多的复杂性。因此,我拒绝发布说明它的代码,因为担心有人会看到这个线程并在他们的软件中实际使用它。

您可能认为重新安排方法调用会起作用(调用id打电话之前name): new StudentBuilder().id(165).name("Jane Doe").build(),但不会。至少在没有明确强制转换的情况下是这样Student: (Student)new StudentBuilder().id(165).name("Jane Doe").build()因为,在这种情况下,PersonBuilder#build正在被调用,其返回类型为Person……这简直让人无法接受!即使它在没有显式强制转换的情况下也能工作,它应该会让您畏缩地知道必须按特定顺序调用构建器的方法。因为如果你不这样做,有些事情就不会起作用......

如果您继续尝试让它发挥作用,还会出现更多问题。即使你确实让它发挥作用,我也不认为它很容易理解,而且肯定不优雅。当然,请随时证明我错了,并在这里发布您的解决方案。

顺便说一句,你还应该问自己什么是abstract建造者?因为,这听起来像是一个矛盾修辞法。

最后,我认为这个问题的范围太大了。答案是特定于领域的,并且在没有您的要求的情况下很难想出。请记住,构建器的一般准则是让它们尽可能简单。

另外,看一下相关问题 https://stackoverflow.com/questions/6734553/java-builder-pattern-and-a-deep-object-hierarchy.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

用于具体实现抽象类的构建器(Joshua Bloch 风格)? 的相关文章

随机推荐