3

I want to implement a builder pattern with static inner classes for lets say classes A with fields (a1, a2, a3), B with fields (b1, b2) and C with fields (c1), whereas all share fields (s1, s2) from super class SuperClass:

public class A extends SuperClass {
    private final String a1;
    ...

    private A(ABuilder builder) {
        super(builder);
        this.a1 = builder.a1;
        ...
    }

    public static class ABuilder extends SuperClassBuilder implements ABuilderInterface {
        private String a1;
        ...

        @Override
        public ABuilder withA1(String a1) {
            this.a1 = a1;
            return this;
        }
        ...

        @Override
        public SuperClass build() {
            return new A(this);
        }
    }
}

Accordingly for B and C the builders just differ that they have their own fields and implement their own interfaces (BBuilderInterface and CBuilderInterface), whereas these interfaces are only defining which methods are to be implemented:

public interface ABuilderInterface extends SuperClassBuilderInterface {
    ABuilderInterface withA1(String a1);
    ...
}
...<interfaces for B and C>

public interface SuperClassBuilderInterface {
   SuperClassBuilderInterface withS1(String s1);
   ...
   SuperClass build();
}

// Usage of the builders:
public SuperClass foo() {
    return new A.ABuilder()
        .withA1(...) // returns ABuilderInterface
        ...
        .withS1(...) // returns SuperClassBuilderInterface
        ...
        .build();
}

public abstract class SuperClass {
private final String s1;
...

protected SuperClass(SuperClassBuilder builder) {
    this.s1 = builder.s1;
    ...
}

protected static abstract class SuperClassBuilder implements SuperClassBuilderInterface {
    private String s1;
    ...

    @Override
    public SuperClassBuilder withS1(String s1) {
        this.s1 = s1;
        return this;
    }
    ...

    @Override
    public abstract SuperClass build();
}
}

Now you can spot the limitation that when I use the builder I have to pay attention to call the with... methods related to the child class first, then chain the ones for the super class, which is not a big of a deal, but still not sure whether good practice. On the other side I could add the with... methods of the children classes to the super class interface all together and then the limitation is gone, but then I have an interface with mixed with... methods of different child classes.

Which one would you prefer/suggest?

radio
  • 897
  • 2
  • 10
  • 25
  • What does `withS1()` return in `ABuilder`? – alayor Aug 28 '17 at 19:21
  • @alayor in ABuilder there is no withS1() since it is using the parent one. The idea is that A, B, C builder contain only their specific with... methods and the shared ones remain in the super builder – radio Aug 28 '17 at 19:35
  • 2
    This is a very common issue. One pattern is to define a generic "self type" on the superclass and have that returned by its methods. – shmosel Aug 28 '17 at 19:45
  • not clear what u mean @shmosel – radio Aug 28 '17 at 19:48
  • Something like [this](https://stackoverflow.com/questions/30639947/bulider-design-pattern-to-make-generic-method-for-methods-having-large-number-of/30645203#30645203) or [this](https://stackoverflow.com/questions/21210870/in-java-can-you-use-the-builder-pattern-with-required-and-reassignable-fields/21211472#21211472)? – Roman Vottner Aug 28 '17 at 20:43
  • ok, it looks like only alternative is to use generics, I must say, that generics would make it not so user friendly, so I would probably need to stick to my current small limitation – radio Aug 28 '17 at 21:15
  • Who's the user here? If you mean the one using the builder, it's perfectly user friendly. He won't even see any generics. – shmosel Aug 29 '17 at 18:21

1 Answers1

5

Modify the superclass's builder to use an F-bound (aka the Curiously Recurring Template Pattern).

public interface SuperClassBuilderInterface<SELF extends SuperClassBuilderInterface<SELF>> {
    SELF withS1(String s1);
    // etc.
    Superclass build();
}

Then you have:

class SuperClassBuilder<SELF extends SuperClassBuilder<SELF>> implements SuperClassBuilderInterface<SELF>

interface ABuilderInterface<SELF extends ABuilderInterface<SELF>> extends SuperClassBuilderInterface<SELF>

class ABuilder extends SuperClassBuilder<ABuilder> implements ABuilderInterface<ABuilder>

Note that the implementation of SuperClassBuilder must contain unchecked casts of the form return (SELF)this;. The type system is theoretically powerful enough to not need this, but the resulting encoding would probably be very ugly (see this) and it's likely not worth it.

EDIT: This is what @shmosel meant

HTNW
  • 27,182
  • 1
  • 32
  • 60
  • I just tried it and it worked, thanks a lot. First I did not like it cause it adds so many Generics, which I don't get how they work at first glance and need to think twice :) but for the caller it is easier, since he will not have any limitations – radio Aug 31 '17 at 20:58
  • btw. is this correct?: `SuperClassBuilder`'s implementation returns SELF as `return (SELF) this;` as you mentioned and `ABuilderInterface`'s method returns SELF and its implementation returns ABuilder – radio Aug 31 '17 at 21:39
  • Yes. I'll actually edit the answer to not use unchecked casts, though. – HTNW Aug 31 '17 at 21:41
  • Whoops, that's not possible. Almost possible, but not really. – HTNW Aug 31 '17 at 21:52