生成器模式与inheritance

我有一个随着inheritance树深化而增加复杂性的对象层次结构。 这些都不是抽象的,因此,它们的所有情况都或多或less地具有复杂性。

由于参数数量非常高,我想使用Builder模式来设置属性,而不是编写几个构造函数。 因为我需要迎合所有的排列,我的inheritance树中的叶子类将具有伸缩式构造函数。

当我在devise中遇到一些问题时,我在这里浏览了一个答案。 首先,让我给你一个简单,浅的例子来说明问题。

public class Rabbit { public String sex; public String name; public Rabbit(Builder builder) { sex = builder.sex; name = builder.name; } public static class Builder { protected String sex; protected String name; public Builder() { } public Builder sex(String sex) { this.sex = sex; return this; } public Builder name(String name) { this.name = name; return this; } public Rabbit build() { return new Rabbit(this); } } } public class Lop extends Rabbit { public float earLength; public String furColour; public Lop(LopBuilder builder) { super(builder); this.earLength = builder.earLength; this.furColour = builder.furColour; } public static class LopBuilder extends Rabbit.Builder { protected float earLength; protected String furColour; public LopBuilder() { } public Builder earLength(float length) { this.earLength = length; return this; } public Builder furColour(String colour) { this.furColour = colour; return this; } public Lop build() { return new Lop(this); } } } 

现在我们有一些代码要继续,我想要构build一个Lop映像:

 Lop lop = new Lop.LopBuilder().furColour("Gray").name("Rabbit").earLength(4.6f); 

这个调用不会被编译,因为最后的链接调用不能被parsing, Builder没有定义方法earLength 。 所以这种方式要求所有的调用都按特定的顺序进行链接,这是非常不切实际的,特别是对于深层次的树。

现在,在我search答案的过程中,我遇到了一个Java Builder类的Subclassing ,它build议使用好奇recursion通用模式(Curiously Recursive Generic Pattern) 。 但是,由于我的层次结构不包含抽象类,所以此解决scheme对我无效。 但是这种方法依赖于抽象和多态的function,这就是为什么我不相信我能适应我的需求。

目前我已经解决的方法是覆盖层次结构中超类Builder所有方法,只需执行以下操作:

 public ConcreteBuilder someOverridenMethod(Object someParameter) { super(someParameter); return this; } 

有了这个方法,我可以保证我正在返回一个实例,我可以发出连锁调用。 虽然这不像Telescoping反模式那样糟糕,但是我们认为这有点“黑客”。

有没有我不知道的另一个解决scheme? 优选与devise模式一致的解决scheme。 谢谢!

这在recursiongenerics中当然是可能的,但是您需要将genericstypes传递给下一个genericstypes。 语法有点繁琐,但你当然可以做到。

 abstract class GenericMammalBuilder // extend this for <B extends GenericMammalBuilder<B>> { // Mammal subtype builders String sex; String name; B ofSex(String sex) { this.sex = sex; return (B)this; } B ofName(String name) { this.name = name; return (B)this; } } final class MammalBuilder // use this to extends GenericMammalBuilder<MammalBuilder> { // build new Mammal instances Mammal build() { return new Mammal(this); } } abstract class GenericRabbitBuilder // extend this for <B extends GenericRabbitBuilder<B>> // Rabbit subtype builders extends GenericMammalBuilder<B> { // eg LopBuilder Color color; B ofColor(Color color) { this.color = color; return (B)this; } } final class RabbitBuilder // use this to build extends GenericRabbitBuilder<RabbitBuilder> { // new Rabbit instances Rabbit build() { return new Rabbit(this); } } 

recursiongenerics的问题是参数必须是一个参数化的子typesIE,如果你有:

 class A<T extends A<T>> {} class B<T extends B<T>> extends A<T> {} class C extends B<C> {} 

你可以做new C(); 但不是new B<B>(); 。 (错误地,你也可以做new B<C>(); ;。)

所以你确实需要某种“临时具体”的课程。 这是一种select你自己的邪恶的场景国际海事组织。

这种forms似乎近乎工作。 这不是很整洁,但它看起来像避免了你的问题:

 class Rabbit<B extends Rabbit.Builder<B>> { String name; public Rabbit(Builder<B> builder) { this.name = builder.colour; } public static class Builder<B extends Rabbit.Builder<B>> { protected String colour; public B colour(String colour) { this.colour = colour; return (B)this; } public Rabbit<B> build () { return new Rabbit<>(this); } } } class Lop<B extends Lop.Builder<B>> extends Rabbit<B> { float earLength; public Lop(Builder<B> builder) { super(builder); this.earLength = builder.earLength; } public static class Builder<B extends Lop.Builder<B>> extends Rabbit.Builder<B> { protected float earLength; public B earLength(float earLength) { this.earLength = earLength; return (B)this; } @Override public Lop<B> build () { return new Lop<>(this); } } } public class Test { public void test() { Rabbit rabbit = new Rabbit.Builder<>().colour("White").build(); Lop lop1 = new Lop.Builder<>().earLength(1.4F).colour("Brown").build(); Lop lop2 = new Lop.Builder<>().colour("Brown").earLength(1.4F).build(); //Lop.Builder<Lop, Lop.Builder> builder = new Lop.Builder<>(); } public static void main(String args[]) { try { new Test().test(); } catch (Throwable t) { t.printStackTrace(System.err); } } } 

尽pipe我已经成功地构build了RabbitLop (在这两种forms中),但是我不能在这个阶段弄清楚如何真正实例化其中一个Builder对象的完整types。

这个方法的本质依赖于Builder方法中的(B) 。 这允许您定义对象的types和Builder的types,并在构造对象时保留它。

如果有人可以找出正确的语法(这是错误的),我将不胜感激。

 Lop.Builder<Lop.Builder> builder = new Lop.Builder<>(); 

如果还有人遇到同样的问题,我build议下面的解决scheme,这个解决scheme符合“优先构造优于inheritance”的devise模式。

父类

它的主要元素是父类Builder必须实现的接口:

 public interface RabbitBuilder<T> { public T sex(String sex); public T name(String name); } 

以下是更改后的父类:

 public class Rabbit { public String sex; public String name; public Rabbit(Builder builder) { sex = builder.sex; name = builder.name; } public static class Builder implements RabbitBuilder<Builder> { protected String sex; protected String name; public Builder() {} public Rabbit build() { return new Rabbit(this); } @Override public Builder sex(String sex) { this.sex = sex; return this; } @Override public Builder name(String name) { this.name = name; return this; } } } 

孩子class

子类Builder必须实现相同的接口(使用不同的genericstypes):

 public static class LopBuilder implements RabbitBuilder<LopBuilder> 

在子类Builder ,引用父Builder Builder的字段:

 private Rabbit.Builder baseBuilder; 

这可以确保父级Builder方法在子级中调用,但是,它们的实现是不同的:

 @Override public LopBuilder sex(String sex) { baseBuilder.sex(sex); return this; } @Override public LopBuilder name(String name) { baseBuilder.name(name); return this; } public Rabbit build() { return new Lop(this); } 

Builder的构造函数:

 public LopBuilder() { baseBuilder = new Rabbit.Builder(); } 

构build子类的构造函数:

 public Lop(LopBuilder builder) { super(builder.baseBuilder); } 

因为你不能使用generics,现在可能的主要任务是以某种方式放松打字。 我不知道以后如何处理这些属性,但是如果使用HashMap将它们存储为键值对呢? 所以在构build器中将只有一个(键,值)包装器方法(或者构build器可能不再需要)。

不利的方面是在处理存储的数据时附加types的铸件。

如果这个案例太松,那么你可以保留现有的属性,但是有一个通用的set方法,它使用reflection和基于'key'名称searchsetter方法。 虽然我认为反思会过度。

我做了一些试验,我发现这对我来说工作的很好。 请注意,我更喜欢在开始时创build实际实例,并且调用该实例上的所有设置器。 这只是一个偏好。

与接受的答案的主要区别在于

  1. 我传递一个表示返回types的参数
  2. 不需要摘要…和最终的build设者。
  3. 我创build了一个'newBuilder'方便的方法。

代码:

 public class MySuper { private int superProperty; public MySuper() { } public void setSuperProperty(int superProperty) { this.superProperty = superProperty; } public static SuperBuilder<? extends MySuper, ? extends SuperBuilder> newBuilder() { return new SuperBuilder<>(new MySuper()); } public static class SuperBuilder<R extends MySuper, B extends SuperBuilder<R, B>> { private final R mySuper; public SuperBuilder(R mySuper) { this.mySuper = mySuper; } public B withSuper(int value) { mySuper.setSuperProperty(value); return (B) this; } public R build() { return mySuper; } } } 

然后一个子类如下所示:

 public class MySub extends MySuper { int subProperty; public MySub() { } public void setSubProperty(int subProperty) { this.subProperty = subProperty; } public static SubBuilder<? extends MySub, ? extends SubBuilder> newBuilder() { return new SubBuilder(new MySub()); } public static class SubBuilder<R extends MySub, B extends SubBuilder<R, B>> extends SuperBuilder<R, B> { private final R mySub; public SubBuilder(R mySub) { super(mySub); this.mySub = mySub; } public B withSub(int value) { mySub.setSubProperty(value); return (B) this; } } } 

和一个子类

 public class MySubSub extends MySub { private int subSubProperty; public MySubSub() { } public void setSubSubProperty(int subProperty) { this.subSubProperty = subProperty; } public static SubSubBuilder<? extends MySubSub, ? extends SubSubBuilder> newBuilder() { return new SubSubBuilder<>(new MySubSub()); } public static class SubSubBuilder<R extends MySubSub, B extends SubSubBuilder<R, B>> extends SubBuilder<R, B> { private final R mySubSub; public SubSubBuilder(R mySub) { super(mySub); this.mySubSub = mySub; } public B withSubSub(int value) { mySubSub.setSubSubProperty(value); return (B)this; } } } 

为了validation它充分的工作,我用这个testing:

 MySubSub subSub = MySubSub .newBuilder() .withSuper (1) .withSub (2) .withSubSub(3) .withSub (2) .withSuper (1) .withSubSub(3) .withSuper (1) .withSub (2) .build();