我认为以这种方式思考建设者不是一个好主意。构建者的层次结构通常会导致令人头痛和脆弱的代码。
减少需要在具体构建器中编写的代码量并重用基础构建器中的逻辑与领域密切相关。开发通用解决方案并不容易。但是,无论如何,让我们尝试看一个例子:
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.