Immutable objects are important to make your code more robust, especially in days of more parallelization. A builder pattern is used when some of the variables of an immutable class are required and some are optional. But this leads to a massive constructor explosion, at least in Java. Today I think I found an improved builder pattern which could be used with no attribute duplication in the builder class and no separate private constructor in the domain class.
Usual Constructors
Here is a normal immutable class with the various necessary constructors for only one optional field ‘age':
public class Person {
private final String name; // required
private final int age; // optional
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name) {
this.name = name;
}
public Person(int age) {
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return age;
}
}
Builder Pattern 1.0
The builder pattern removes the need of various constructor combinations:
public class Person {
private final String name; // required
private final int age; // optional
private Person(PersonBuilder pb) {
this.age = pb.age;
this.name = pb.name;
}
public String getName() {
return this.name;
}
public int getAge() {
return age;
}
}
public class PersonBuilder {
private String name;
private int age;
public PersonBuilder name(String name) {
this.name = name;
return this;
}
public PersonBuilder age(int age) {
this.age = age;
return this;
}
public Person create() {
return new Person(this);
}
}
The usage is:
Person p = new PersonBuilder().
name("testing").
age(20).
create();
Builder Pattern 2.0
Now my builder pattern with less overhead. Of course in real world examples you won’t have only one optional field making the savings more obvious. The Builder Pattern 2.0 uses a static embedded subclass for the builder and still uses (package) protected fields. As you can see this solution is only ~5 lines more than the original immutable object without the constructors as it just moves the setters into a separate class:
public class Person {
String name; // required
int age; // optional
public String getName() {
return this.name;
}
public int getAge() {
return age;
}
public static class BEGIN extends Person {
public BEGIN name(String name) {
this.name = name;
return this;
}
public BEGIN age(int age) {
this.age = age;
return this;
}
public Person END() {
return this;
}
} // end of builder class
} // end of domain class
The usage is similar to the original builder pattern:
Person p = new Person.BEGIN().
name("testing").
age(20).
END();
Keep in mind that this solution has the ‘drawback’ of no unnecessary object creation involved like builder pattern 1.0. And therefor the END method is not thread-safe unlike the create method. (You can fix that via this.clone() within END, not sure if you like that). Also I think for those cases you probably need more something like a factory. As noted in the comments the builder class START should be renamed to Builder and then even better create a public static method ala ‘Builder Start() { return new Builder(); }’ where you then can avoid the ‘new’ when using it.
Improvement: Builder Pattern 2.1
After the comments and having this implemented in production I observed drawbacks. E.g. that you don’t have to call the END method at all as the subclass is also accepted. And that you could theoretically just downcast a Person object to its builder and change the variables again. The simplest solution is to use composition instead of inheritance like we do with our AlgorithmOptions object at GraphHopper, this way we can also use private fields again.
Conclusion
This new builder pattern is suited if a method has several arguments with some of them optional. You can move these arguments into a separate class and use this pattern to avoid code duplication like I’ll probably do for some classes in GraphHopper. For everyone in love with magic (unlike me) they can also have a look into the project lombok as noted in the comments. Still the best thing would be to have something like this directly in Java and being able to write:
Person p = new Person(name="testing", age=20);
Filed under:
Java