gpt4 book ai didi

java - 用于具体实现抽象类的生成器(Joshua Bloch 风格)?

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:55:12 25 4
gpt4 key购买 nike

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

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

和:

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

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

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

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

和:

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

绝妙的宝贝!怎么办?也许您想添加一个类(class)来代表学生。你做什么工作?你扩展人吗?当然,这是有效的。采取更“奇怪”的路线并尝试聚合怎么样?是的,您也可以这样做……您的选择会影响您最终如何实现构建器。假设您坚持传统路径并扩展 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 的返回类型为 StudentBuilder。现在,您提前考虑并意识到,如果有任何东西扩展 StudentBuilder,您会希望它完全返回其他东西……这可行吗?是的,使用泛型。然而,它非常丑陋,并且引入了相当多的复杂性。因此,我拒绝发布说明它的代码,因为担心有人会看到这个线程并实际在他们的软件中使用它。

您可能认为重新排列方法调用会起作用(在调用 name 之前调用 id):new StudentBuilder().id(165).name("Jane Doe").build(),但它不会。至少不是没有对 Student 的显式转换:(Student)new StudentBuilder().id(165).name("Jane Doe").build() 因为,在这种情况下,PersonBuilder#build 被调用,返回类型为 Person...这是 Not Acceptable !即使它在没有显式强制转换的情况下也能工作,它应该会让您畏缩,因为它知道必须按特定顺序调用构建器的方法。因为如果你不这样做,有些东西将无法工作......

如果您继续尝试让它工作,将会出现更多问题。即使你确实让它工作了,我也不认为它会很容易理解,当然也不优雅。当然,请随时证明我的错误并在此处发布您的解决方案。

顺便问一下,您还应该问问自己什么是抽象 构建器?因为,这听起来很矛盾。

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

另外,看看 related question .

关于java - 用于具体实现抽象类的生成器(Joshua Bloch 风格)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13255985/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com