Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I define a builder pattern hierarchy where the setters can be called in any order

Consider the abstract Data class with an abstract Builder:

abstract class Data {

    abstract static class Builder<T extends Data> {

        private String one;

        protected Builder() {
            this.one = null;
        }

        public final Builder<T> withOne(final String value) {
            this.one = value;
            return this;
        }

        protected abstract T build();
    }

    private final String one;

    protected Data(final Builder<? extends Data> builder) {
        this.one = builder.one;
    }

    public final String getOne() {
        return this.one;
    }
}

The class is extended, which also includes its own extended Builder:

public final class Extension extends Data {

    public static final class ExtensionBuilder extends Data.Builder<Extension> {

        private String two;

        private ExtensionBuilder() {
            super();
            this.two = null;
        }

        public static final ExtensionBuilder newInstance() {
            return new ExtensionBuilder();
        }

        public final ExtensionBuilder withTwo(final String value) {
            this.two = value;
            return this;
        }

        public final Extension build() {
            return new Extension(this);
        }
    }

    private final String two;

    private Extension(final ExtensionBuilder builder) {
        super(builder);
        this.two = builder.two;
    }

    public final String getTwo() {
        return this.two;
    }
}

The Extension object gets all methods from both classes. However the order the setter methods is called is important. This is OK:

Extension one = Extension.ExtensionBuilder
                .newInstance()
                .withTwo("two")
                .withOne("one")
                .build();

Whereas this produces a compilation error:

Extension two = Extension.ExtensionBuilder
                .newInstance()
                .withOne("one")
                .withTwo("two")
                .build();

I know the reason why, the withOne() setter has downcast the type of Builder object from the concrete class to the abstract one. I would like to know what I need to do to solve this, so setters can be called in any order.

like image 244
Marcus MacWilliam Avatar asked Jan 24 '18 18:01

Marcus MacWilliam


People also ask

What is the Builder pattern?

The Builder Pattern is another creational design pattern that uses a Builder to create objects by assembling their parts. Builder pattern allows for the construction of complex objects by using a step-by-step process.

How to implement builder pattern in Java?

Implementation : In Builder pattern, we have a inner static class named Builder inside our Server class with instance fields for that class and also have a factory method to return an new instance of Builder class on every invocation. The setter methods will now return Builder class reference.

What is the difference between factory method and builder pattern?

The Factory Method pattern is a creational design pattern that uses factories to create objects without specifying the exact type of object to be created. The Builder Pattern is another creational design pattern that uses a Builder to create objects by assembling their parts.

What are the setter methods in fluent builder?

The Builder has the setter methods that construct the complex object. Typically, each method returns void. Only the Build method returns the type you are building. The Fluent builder pattern is a creational pattern that makes object creation more straightforward and readable.


1 Answers

If you don't want (or can't) override the withOne method in the ExtensionBuilder class as suggested in this answer, you can use a recursively bounded generic type for your builder:

abstract static class Builder<T extends Data, B extends Builder<T, B>> {

    private String one;

    protected Builder() {
        this.one = null;
    }

    public final B withOne(final String value) {
        this.one = value;
        return (B) this;
    }

    protected abstract T build();
}

Then, declare the ExtensionBuilder class as follows:

public static final class ExtensionBuilder 
    extends Data.Builder<Extension, ExtensionBuilder>

This will allow you to use the most concrete builder's methods in any order, because the compiler now knows the static type of the concrete builder.

EDIT: As pointed out by @Radiodef in the comments, there's an alternative to the cast, which consists of declaring a protected abstract B getThis() method in the Builder class:

abstract static class Builder<T extends Data, B extends Builder<T, B>> {

    private String one;

    protected Builder() {
        this.one = null;
    }

    protected abstract B getThis();

    public final B withOne(final String value) {
        this.one = value;
        return getThis(); // no need to cast now
    }

    protected abstract T build();
}

And of course in the ExtensionBuilder you should implement it as:

@Override
protected ExtensionBuilder getThis() { return this; }

Here's the source: http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ205

like image 68
fps Avatar answered Sep 21 '22 08:09

fps