Concepts

Adding Data To Objects – Another Builder Pattern

While reading up on builders to write these posts, I came across an interesting blog by Per Lundholm which demonstrated another builder pattern that can be used. This solves the problem of having some fields which are optional and others which are mandatory. As it stands, we are treating all fields the same in the builder, and introducing any specific behaviour we want to for optional fields when we construct the object.

In the blog, Per investigates different ways to differentiate between mandatory and optional fields, so that whoever uses the builder knows which fields they need to populate. His first suggestion is to add assertions into the build method, which would let us know at runtime if a mandatory field was missing. The build method in our example would become

        public Wizard build() {
            assert wizard.name != null;
            assert wizard.age != null;
            assert wizard.house != null;
            assert wizard.yearGroup != null;
            return wizard;
        }

This is a potential solution, but delays the feedback to runtime. Ideally, we would know at compile time that something was wrong, to shorten that feedback loop.

The solution he proposes is interesting, and I will apply it to our example here to try and make it clear. He uses a series of internal interfaces, one for each mandatory field, and makes his Builder implement all of them.

class Wizard {
    private String name;
    private int age;
    private House house;
    private int yearGroup;
    private int apparitionLicenseNumber;

    public interface AddName {
        AddAge withName(String name);
    }

    public interface AddAge {
        AddHouse withAge(int age);
    }

    public interface AddHouse {
        AddYearGroup withHouse(House house);
    }

    public interface AddYearGroup {
        AddOptionals withYearGroup(int yearGroup);
    }

    public interface AddOptionals {
        AddOptionals withApparitionLicenseNumber(int licenseNumber);

        Wizard build();
    }

    static class Builder implements AddName, AddAge, AddHouse, AddYearGroup, AddOptionals {
        private Wizard wizard = new Wizard();

        public static Builder aWizard() {
            return new Builder();
        }

        public AddAge withName(String name) {
            wizard.name = name;
            return this;
        }

        public AddHouse withAge(int age) {
            wizard.age = age;
            return this;
        }

        public AddYearGroup withHouse(House house) {
            wizard.house = house;
            return this;
        }

        public AddOptionals withYearGroup(int yearGroup) {
            wizard.yearGroup = yearGroup;
            return this;
        }

        public AddOptionals withApparitionLicenseNumber(int apparitionLicenseNumber) {
            wizard.apparitionLicenseNumber = apparitionLicenseNumber;
            return this;
        }

        public Wizard build() {
            return wizard;
        }
    }
}

Now, if we were to try and create the object without including the age, for example, the compiler will complain (and, if using an IDE we’d instantly get told we’re doing something wrong).

public class WizardTest {
    ā€¦
    Wizard harry = aWizard().withName("Harry Potter")
                            .withHouse(House.GRYFFINDOR)
                            .withYearGroup(3)
                            .build();
    ā€¦
}

The above code would not compile, because the withHouse method can only be applied to an object of type AddHouse, whereas the withName method returns an object of type AddAge. Only once the age is added can we even call withHouse! However, since the build method can be called on any object of type AddOptionals, we can call build() after adding year group, or after adding license number. If we had any further optional fields, they could also be added to this final AddOptionals interface (as the name suggests!) so that the build method can be invoked at any point.

So there we have it, slightly complex set up but solves the problem of differentiating between mandatory and optional fields. Once you get your head around how it works, it’s actually quite a neat trick!

4 thoughts on “Adding Data To Objects – Another Builder Pattern

Leave a reply to perlundholm Cancel reply