The builder pattern and a large number of mandatory parameters

Solution 1:

You can use a Step Builder if you have many mandatory parameters. In short: you define an interface for every single mandatory parameter and a builder method returns the next mandatory builder interface or the builder itself for optional methods. The builder remains a single class which implements all the interfaces.

interface StepB {
    StepBuilder b(String b);
}

interface StepA {
    StepB a(String a);
}

final class StepBuilder implements StepA, StepB {
    private String a;
    private String b;
    private String c = "";

    private StepBuilder() {
    }

    static StepA with() {
      return new StepBuilder();
    }

    // mandatory, from StepA
    @Override
    StepB a(String a) {
        this.a = a;
        return this;
    }

    // mandatory, from StepB
    @Override
    StepBuilder b(String b) {
        this.b = b;
        return this;
    }

    // optional
    StepBuilder c(String c) {
        this.c = c;
        return this;
    }

    Product build() {
        return new Product(a, b, c);
    }
}

Usage:

StepBuilder.with().a("hello").b("world").build();

// or with the optional parameter c
StepBuilder.with().a("hello").b("world").c("!").build();

Languages like Kotlin and Scala are more convenient here, since they offer named parameters with default values.

Solution 2:

However, I've been struggling lately to understand how the pattern is of any benefit when all your parameters are mandatory (or at least the vast majority are).

The fluent builder pattern is still beneficial:

  1. Its more readable - it effectively allows named parameters so that the call isn't just a long list of unnamed arguments

  2. Its unordered - this lets you group arguments together into logical groups, either as part of a single builder setter call or simply by letting you use a natural order to calling the builder setter methods that make the most sense of this particular instantiation.


Widget example = new Widget.Builder(req1, req2, req3,req4,req5,req6,req7,req8)
                               .addOptional(opt9)
                               .build();

becomes grouped as follows:

Object1 group1  = new Object1(req1, req2, req3, req4);
Object2 group2  = new Object2(req5, req6);
Widget example2 = new Widget.Builder(group1, group2, req7, req8)
                            .addOptional(opt9)
                            .build();

While having separate objects simplifies things quite a bit, it also makes things a little difficult to follow if one is not familiar with the code. One thing I considered was moving all parameters into their own addParam(param) methods and then performing validation on required parameters in the build() method.

I would favor a hybrid when appropriate or natural. It doesn't have to be all in constructor or each param has its own addParam method. Builder gives you flexibility to do one, the other, in-between, or a combo:

Widget.Builder builder = new Widget.Builder(Widget.BUTTON);

builder.withWidgetBackingService(url, resource, id);
builder.withWidgetStyle(bgColor, lineWidth, fontStyle);
builder.withMouseover("Not required");

Widget example = builder.build();

Solution 3:

I've been struggling lately to understand how the pattern is of any benefit when all your parameters are mandatory

The pattern eases creation of immutable classes and promotes readable code. Consider the Person class below (with a conventional constructor and a builder).

public static class Person {

    private static final class Builder {
        private int height, weight, age, income, rank;
        public Builder setHeight(final int height) { this.height = height; return this; }
        public Builder setWeight(final int weight) { this.weight = weight; return this; }
        public Builder setAge(final int age) { this.age = age; return this; }
        public Builder setIncome(final int income) {    this.income = income; return this; }
        public Builder setRank(final int rank) { this.rank = rank; return this; }
        public Person build() { return new Person(this); }
    }

    private final int height;
    private final int weight;
    private final int age;
    private final int income;
    private final int rank;

    public Person(final int height, final int weight, final int age, final int income, final int rank) {
        this.height = height; this.weight = weight; this.age = age; this.income = income; this.rank = rank;
    }

    private Person(final Builder builder) {
        height = builder.height; weight = builder.weight; age = builder.age; income = builder.income; rank = builder.rank;
        // Perform validation
    }

    public int getHeight() { return height; }
    public int getWeight() { return weight; }
    public int getAge() { return age; }
    public int getIncome() { return income; }
    public int getRank() {  return rank; }

}

Which method of construction is easier to comprehend?

final Person p1 = new Person(163, 184, 48, 15000, 23);
final Person p2 = new Person.Builder().setHeight(163).setWeight(184).setAge(48).
    setIncome(15000).setRank(23).build();

One means of getting around this has been to logically group the parameters being passed in to their own classes

Sure, this is the principle of cohesion and should be adopted irrespective of object construction semantics.

Solution 4:

One advantage of the Builder Pattern that I rarely (if ever) see promoted is that it can also be used to conditionally construct the object, for instance only if all mandatory parameters are correct or if other required resources are available. In that respect they offer similar benefits to a static factory method.

Solution 5:

I think this would be appropriate in case you have large mandatory values, though number of interfaces would increase but code would be clean

public class PersonBuilder implements NamePersonBuilder, LastNamePersonBuilder, 
                                  BirthDatePersonBuilder, FinalPersonBuilder {

private String name;
private String lastName;
private Date birthDate;
private String phoneNumber;

/**
 * Private constructor to force the use of the factroy method
 */
private PersonBuilder() {
}

/**
 * Creates a new person builder
 */
public static NamePersonBuilder aPerson() {
    return new PersonBuilder();
}

public LastNamePersonBuilder withName(String aName) {
    name = aName;
    return this;
}

public BirthDatePersonBuilder withLastName(String aLastName) {
    lastName = aLastName;
    return this;
}

public FinalPersonBuilder withBirthDate(Date aBirthDate) {
    birthDate = aBirthDate;
    return this;
}

public FinalPersonBuilder andPhoneNumber(String aPhoneNumber) {
    phoneNumber = aPhoneNumber;
    return this;
}

public Person build() {
    // The constructor and setters for Person has default scope
    // and is located in the same package as the builder
    Person p = new Person();
    p.setName(name);
    p.setLastName(lastName);
    p.setBirthDate(birthDate);
    p.setPhoneNumber(phoneNumber);
    return p;
}

interface NamePersonBuilder {
    LastNamePersonBuilder withName(String aName);
}

interface LastNamePersonBuilder {
    BirthDatePersonBuilder withLastName(String aLastName);
}

interface BirthDatePersonBuilder {
    FinalPersonBuilder withBirthDate(Date aBirthDate);
}

interface FinalPersonBuilder {
    FinalPersonBuilder andPhoneNumber(String aPhoneNumber);
    Person build();
}}

This will force the user to set all mandatory values and also force the order that the values are set. So to construct a person this will be the resulting code:

PersonBuilder.aPerson()
    .withName("Name")
    .withLastName("LastName")
    .withBirthDate(new Date())
    .build();

Checkout this Reference: Builder Pattern with Twist