Misuse of Builder pattern

Ürgo Ringo
Javarevisited
Published in
3 min readOct 16, 2023

--

Photo by Patrick Tomasso on Unsplash

According to the original definition Builder pattern intent is:

Separate the construction of a complex object from its representation so that the same construction process can create different representations.

However, these days I see Builder in Java mostly used as a band-aid for lack of named parameters in Java language.

With Lombok, the code will look something like this:

@Builder
@Value
public class Customer {
Instant signedUpAt;
LocalDate dateOfBirth;
String firstName;
String lastName;
String email;
String phone;
String addressFirstLine;
String addressSecondLine;
String city;
String postalCode;
String countryCode;
}

Indeed, without Builder it will be quite easy to accidentally swap some parameters around. What makes things worse is that often many parameters use the same primitive type. This is an example of positional connascence which is the worst kind of static connascence (also recommend watching the talk from Jim Weirich on Connascence).

Unfortunately, Builder has a few unpleasant downsides

The first downside is that Builder makes it hard to understand what parameters are required or what combination of parameters is valid. If I miss a required parameter I may only later hit an NPE when trying to access a field that was supposed to be not null. With primitive booleans it can be even harder to discover as fields just get initialized to false.

The second downside is that this solution makes it easy to avoid solving root problems which are:

Split the class up into smaller pieces

According to Yegor Bugayenko an object should not have more than four attributes. This may seem quite strict but is a good ideal goal nevertheless.

In the example above splitting is quite easy:

public class Customer {
Instant signedUpAt;
LocalDate dateOfBirth;
ContactInfo contactInfo;
}

public class ContactInfo {
PersonName name;
String email;
String phone;
Address address;
}

public class Address {
String firstLine;
String secondLine;
String city;
String postalCode;
String countryCode;
}

public class PersonName {
String firstName;
String lastName;
}

Sometimes splitting is easy but often it can be quite hard. One approach to finding effective ways to split things up is by looking for groups of fields that are related to some specific behavior.

Introducing value objects

Primitive obsession a.k.a stringly typed code is another issue that can be covered up by using Builders.

The proper solution is introducing domain-specific Value Objects. In addition to type safety, these Value Objects are also good for checking basic validity and implementing simple operations instead of using procedural utility classes.

This is how ContactInfo will look after replacing all String types with Value Objects:

public class ContactInfo {
PersonName name;
Email email;
Phone phone;
Address address;
}

Kotlin has recognized this need for simple value wrappers and has inline value classes that allow the creation of such types with little effort and no runtime overhead.

Summary

By reducing the number of fields inside a class and replacing primitive types with Value Objects I have made my code much more expressive. As a side-effect, there is no need for a Builder to construct objects safely as I can rely on the compiler doing the checking for me.

Of course, Builder is still an option for cases where I need to construct an object in various different ways. Some kind of Query objects that allows me to find data using various combination of parameters is one good example.

--

--

Ürgo Ringo
Javarevisited

In software engineering for 20+ years. Worked as IC/tech lead at Wise. Currently tech lead at Inbank. Interests: product engineering and org culture.