我想在我即将开展的一些工作中使用构建器模式,它在层次结构中有几个类。基类将至少有9个字段可以启动,而各个子类可以在每个字段之间添加2-4个更多字段。由于这个确切的原因,这将很快失控,建造者模式对我很有吸引力。我在书籍和文章中初步了解了构建器模式。他们很有帮助,但对如何扩展这种模式一无所知。我试图自己实现这个,但是我遇到了每个子类的构造函数的问题,因为我没有得到如何将构建器中收集的数据传递给超类。我在SO上找了一些答案,这就是我找到的。
这个来自SO 24243240,其中给出了如何使用抽象构建器扩展抽象类的示例。它也基于此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;
}
}
}
然后在客户端代码中,它看起来像......
ConcreteA baz = new ConcreteA.Builder().withFoo("foo").withS("bar").withI(0).build();
我喜欢这个例子,因为它允许你轻松扩展这些类,但在我看来,这也违背了使用构建器模式的目的,因为方法withS(String s)
和withI(int i)
很像二传手术方法。此外,此方法将基类和构建器类的字段保留为受保护而不是私有。
此处来自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
}
}
我意识到你可以按照我在这里展示的方式实例化一个对象,但我还有其他方法吗?这可能是工厂要去的地方吗?也许我只是对构建器模式有错误的假设。 :)如果是这样的话,还有更好的方向吗?
答案 0 :(得分:0)
你的第一个例子并不差,但我认为这不是你想要的。
我仍然不确定你想要什么,但看到你的例子不适合你,我想我会给你一两个我自己的。 :)
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;
}
在你的例子中。
然而,对于变量和子类的数量,这种情况很快就会失控。一个替代方案,就是使用动态变量,看看这个:http://martinfowler.com/apsupp/properties.pdf Martin Fowler写了一篇很好的文章,详细说明了所有的优点和缺点。
无论如何,这是我的第二个例子:
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;
}
}
这里的魔力是,您(可能)不再需要所有这些子类。 如果这些子类&#39;行为不会改变,但只有他们的变量,你应该使用这样的系统。 我强烈建议你阅读Martin Fowler关于这个主题的文章,虽然有很好的地方和不好的地方,但我认为这是一个很好的。
我希望这会让你更接近答案,祝你好运。 :)