1

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]
    }
}
3

6 Answers 6

2

According to Joshua Bloch, author of Effective Java, item 2 basically says there are three common ways to create objects in the language:

  1. User overloaded constructors to set required fields, and mutators to set optional fields.

    public class Example {
      private String first;
      private String second;
    
      //first is only required field
      public Example(final String first){
        this.first = first;
      }
    
      //Can't make a constructor just for second field
    
      //first and second are both required fields
      public Example(final String first, final String second){
        this.first = first;
        this.second = second;
      }
      //mutators for optional fields
    }
    
  2. Use JavaBeans style mutators and accessors, not concerned too much about mutability enforcement at compile time.

    public class Example {
      private String first;
    
      public void setFirst(final String first){
        this.first = first;
      }
    
      public String getFirst(){
        return first;
      }
    }
    
  3. Use the Builder design pattern

    public class Example {
      private String first;
      private String second;
    
      private Example(Builder builder){
        this.first = builder.first;
        this.second = builder.second;
      }
    
      @Override
      public String toString(){
        StringBuilder sb = new StringBuilder();
        sb.append("Example [first=").append(first).append(", second=").append(second).append("]");
        return sb.toString();
      }
    
      //Add getter methods as needed, but no setters if you want immutable objects
    
      public static class Builder{
        private String first;
        private String second;
    
        //required fields go in the constructor
        public Builder(final String first){
          this.first = first;
        }
    
        //optional fields have setter type methods
        public Builder second(String value){
          this.second = value;
        }
    
        public Example build(){
          return new Example(this);
        }
      }
    
      public static void main(String[] args){
        //builder with required first field
        Builder builder = new Example.Builder("first");
        //optional second field - calls can be chained
        builder.second("second");
        Example example = builder.build();
        System.out.println(example.toString());
      }
    }
    

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.

1

With your design, the programmer must call all yours methods in the order you define. In this case, if you have only a few parameters, a constructor should be perfect.

What I prefer in the builder pattern is that you can call the methods in the order the developer want.

The build method should validate the required values.

public class Builder {
    private String first;
    ...

    public Builder first(String first) {
        this.first = first;
        return this;
    }

    public Example build() {
        if(first == null) throw new IllegalArgumentException("first can't be null");
        return new Example(this.first);
    }
1

I'll add to @kpw's answer with a habit of my own that might be instructive/useful. If a field is truly required in your object, then it should be required in the constructor.

In general, I dislike the trend of having Builder class for everything, but I am strongly in favor of the "chained" or "fluid" style of method invocation that they present. By designing your class as its own builder, you get the nice features of both design patterns, in one class.

public class Thing
{
    protected String one = null ; // required
    protected String two = null ; // optional

    private Thing() {} // Explicitly prevent construction without "one"

    public Thing( String one )
    {
        this.setOne( one ) ;
    }

    public String getOne()
    { return this.one ; }

    public Thing setOne( String s )
    {
        if( s == null ) throw new IllegalArgumentException() ;
        this.one = s ;
        return this ;
    }

    public String getTwo()
    { return this.two ; }

    public Thing setTwo( String s )
    {
        this.two = s ;
        return this ;
    }

    public SomeReturnedStuff doStuff()
    {
        // is guaranteed to have this.one
        // optionally acts on this.two
    }
}

Usage of this pattern:

Thing nope = new Thing() ; // forbidden at compile time
Thing nopeAgain = new Thing(null) k // throws exception at runtime
Thing alpha = new Thing( "un" ) ;
alpha.doStuff() ; // does stuff with "un"
Thing beta = new Thing( "uno" )
        .setTwo( "dos" ) ;
beta.doStuff() ; // does stuff with "deux" and "dos"
alpha.setOne( "ichi" )
     .setTwo( "ni" )
     .doStuff()
     ;

As the last example shows, this pattern even lets you reuse an instance by "rebuilding" it with its own mutator methods.

YMMV but I, for one, have adopted this pattern universally. Enjoy!

0

I like this builder.

Do you really need a Builder in your case ? Liking a pattern is not enough to justify its use.

Upto 4-5 arguments, creating instances of your class directly by the constructor greatly simplify your code, plus allows you to make your class immutable.

public final class Example {
    private final String first;
    private final String second;
    private final String third;
    private final String fourth;
    private final String fifth;

    public Example(final String first, final String second, final String third, final String fourth, final String fifth) {
        ...
    }

    @Override
    public final String toString() {
        return "Example [first=" + first + ", second=" + second + ", third=" + third + ", fourth=" + fourth + ", fifth=" + fifth + "]";
    }

    public final String getFirst() { 
        return first; 
    }

    ...
}
5
  • Immutability is just as achievable using a Builder object as with direct construction.
    – Jules
    Commented Sep 3, 2015 at 1:51
  • 1
    @Jules How would you do to achieve immutability with a builder ?
    – Spotted
    Commented Sep 3, 2015 at 5:40
  • 2
    @Spotted The builder is mutable, the class being built is immutable. It's only constructed once you call build() Commented Sep 3, 2015 at 8:01
  • @BenAaronson Ok got it. Seems extra unnecessary code for me, except if one of the requirements is to allow the user to construct objects in a "fluent-style".
    – Spotted
    Commented Sep 3, 2015 at 8:31
  • @Spotted Yeah. Not clear if the question is over-simplifying, but I certainly don't see a use for it in that case. Commented Sep 3, 2015 at 8:34
0

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?

No. What you've done here is implement a state machine with compile time enforcement. To make that work, you are going to need an interface for each unique state. At a minimum, that's going to require one state per required field.

0

I wouldn't touch codes like that with a 10-foot pole. I get that you are probably trying to solve some problem and this maybe an abstract of it. But if any code get that convoluted then you're probably heading down the wrong path. My feeling is that one builder ought to be enough, and you can check for required fields in the build function. You can also try multiple build functions:

...
class ExampleBuilder {
    private String first;
    ...
    public Example buildFirst() {
        if(first == null) throw new IllegalArgumentException("first can't be null");
        return new Example(this);
    }
    ...
}
1
  • 2
    Should the object not check its own validity? I would bury those concerns in the object being built.
    – user22815
    Commented Mar 6, 2015 at 0:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.