I'm looking for a more abstract pattern for builders that handles required
fields without the need of writing a validator that checks if all requried fields are set.
I like this builder. But is there a way to make the interfaces more abstract
so that I don't need to create a new interface for each required field?
public class Example {
private String first;
private String second;
private String third;
private String fourth;
private String fifth;
public static RequiredSecond builder(String first) {
return new ExampleBuilder(first);
}
public interface RequiredSecond {
RequiredThird withSecond(String second);
}
public interface RequiredThird {
RequiredFourth withThird(String third);
}
public interface RequiredFourth {
Build withFourth(String fourth);
}
public interface Build {
Build withFifth(String fifth);
Example build();
}
private static class ExampleBuilder implements RequiredSecond, RequiredThird, RequiredFourth, Build {
Example example = new Example();
public ExampleBuilder(String first) {
example.setFirst(first);
}
@Override
public Build withFifth(String fifth) {
example.setFifth(fifth);
return this;
}
@Override
public Example build() {
return example;
}
@Override
public Build withFourth(String fourth) {
example.setFourth(fourth);
return this;
}
@Override
public RequiredFourth withThird(String third) {
example.setThird(third);
return this;
}
@Override
public RequiredThird withSecond(String second) {
example.setSecond(second);
return this;
}
}
public String getFirst() {
return first;
}
public void setFirst(String first) {
this.first = first;
}
public String getSecond() {
return second;
}
public void setSecond(String second) {
this.second = second;
}
public String getThird() {
return third;
}
public void setThird(String third) {
this.third = third;
}
public String getFourth() {
return fourth;
}
public void setFourth(String fourth) {
this.fourth = fourth;
}
public String getFifth() {
return fifth;
}
public void setFifth(String fifth) {
this.fifth = fifth;
}
@Override
public String toString() {
return "Example [first=" + first + ", second=" + second + ", third=" + third + ", fourth=" + fourth +
", fifth=" + fifth + "]";
}
}
public class Labb {
public static void main(String[] args) {
// All required including optional
Example ex1 = Example.builder("first").withSecond("second").withThird("third").withFourth("fourth").withFifth("optional fith").build();
System.out.println(ex1);
// Output: Example [first=first, second=second, third=third, fourth=fourth, fifth=optional fith]
// All required excluding optional
Example ex2 = Example.builder("first").withSecond("second").withThird("third").withFourth("fourth").build();
System.out.println(ex2);
// Output: Example [first=first, second=second, third=third, fourth=fourth, fifth=null]
}
}
Best Answer
According to Joshua Bloch, author of Effective Java, item 2 basically says there are three common ways to create objects in the language:
User overloaded constructors to set required fields, and mutators to set optional fields.
Use JavaBeans style mutators and accessors, not concerned too much about mutability enforcement at compile time.
Use the Builder design pattern
The primary advantage of using the Builder pattern is that all built objects, instances of Example in this case, can be guaranteed immutable at compile time.
It is not clear to me why you have an interface for each field in the final built object, nor why they are public interfaces. This appears to me to be incorrect, but I am not sure.