32

Implement the builder pattern prior to Java 8 has lots of tedious, nearly duplicated code; the builder itself is typically boilerplate code. Some duplicate code detectors consider nearly each method of a pre-Java 8 builder as a copy of every other method.

Consider the following pre-Java 8 builder pattern:

public class Person {

    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

public class PersonBuilder {
    
    private static class PersonState {
        public String name;
        public int age;
    }
    
    private PersonState  state = new PersonState();
    
    public PersonBuilder withName(String name) {
        state.name = name;
        return this;
    }
    
    public PersonBuilder withAge(int age) {
        state.age = age;
        return this;
    }
    
    public Person build() {
        Person person = new Person();
        person.setAge(state.age);
        person.setName(state.name);
        state = new PersonState();
        return person;
    }
}

How can the builder pattern be implemented using Java 8?

Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
SpaceTrucker
  • 13,377
  • 6
  • 60
  • 99
  • 4
    The builder pattern is mainly useful when you want to build immutable objects (i.e. your class does not have setters). If you have a mutable class, why would you want to use the builder pattern, instead of just calling setters? – Jesper Jul 31 '15 at 20:40
  • @Jesper Most of the usages I have seen for builders is for creating entities like JPA objects. They usually have setters. So my answer below could be useful. But if you have something good to say about builders for immutable objects using java-8, then go ahead and write another answer. – SpaceTrucker Jul 31 '15 at 20:44
  • 6
    @Jesper because you might have 9 optional parameters during construction and writing a constructor variation for every combination is either really ugly and tedious (9^2 combinations) or impossible (two params have same type) – bcorso Jul 31 '15 at 20:44
  • @SpaceTrucker you should also check out Google's `auto` library. It provides auto-generated builders by using annotations (works on pre java-8 as well) : https://github.com/google/auto/tree/master/value#other-preconditions-or-preprocessing – bcorso Jul 31 '15 at 20:47
  • 3
    Why not just call the setters then? You don't need a builder for mutable objects. There's no special reason why this would be useful for JPA entities (they work the same as POJOs) and calling builder methods or setter methods makes no difference, however many or few properties there are. You can set the properties via setters, you don't need constructors. – Jesper Jul 31 '15 at 20:48
  • 5
    @Jesper, what if you need all parameters during the construction of an Object for it to be in a valid state. Using a builder ensures the Object has all of it's parameters before it can be used. Using setters alone, the Object would be in an invalid state between calls to setters. – bcorso Jul 31 '15 at 20:49
  • 1
    @Jesper The builder pattern is useful in two more scenarios: a. if you want to create several, slightly different objects and b. if your construct your object across several method calls. – biziclop Jul 31 '15 at 20:49
  • @biziclop I agree with point a, but with point b: you could also just call setters from different methods. – Jesper Jul 31 '15 at 20:52
  • @Jesper Yes, but there is no guarantee that someone wouldn't accidentally call a business method. Imagine that you're building an object representing a connection: you first need to set the address and other parameters and once all that is finished then and only then can you call the `open()` method. If you're only passing a builder along, it is guaranteed that no-one can call the `open()` method before the connection is fully configured, because the builder hasn't got such a method. – biziclop Jul 31 '15 at 20:55
  • @Jesper not sure if you still care, but the particular case of setter (Java Bean) pattern vs builder pattern is discussed here if you're interested: http://stackoverflow.com/a/1953567/1290264 – bcorso Aug 01 '15 at 01:03
  • 3
    @bcorso Thanks, I know the builder pattern. My point was that the application of the pattern as SpaceTrucker shows in his code above, if you take this code literally, doesn't add much to just creating an instance of `Person` and call setters on it. – Jesper Aug 01 '15 at 05:23

6 Answers6

96

The GenericBuilder

The idea for building mutable objects (immutable objects are addressed later on) is to use method references to setters of the instance that should be built. This leads us to a generic builder that is capable of building every POJO with a default constructor - one builder to rule them all ;-)

The implementation is this:

public class GenericBuilder<T> {

    private final Supplier<T> instantiator;

    private List<Consumer<T>> instanceModifiers = new ArrayList<>();

    public GenericBuilder(Supplier<T> instantiator) {
        this.instantiator = instantiator;
    }

    public static <T> GenericBuilder<T> of(Supplier<T> instantiator) {
        return new GenericBuilder<T>(instantiator);
    }

    public <U> GenericBuilder<T> with(BiConsumer<T, U> consumer, U value) {
        Consumer<T> c = instance -> consumer.accept(instance, value);
        instanceModifiers.add(c);
        return this;
    }

    public T build() {
        T value = instantiator.get();
        instanceModifiers.forEach(modifier -> modifier.accept(value));
        instanceModifiers.clear();
        return value;
    }
}

The builder is constructed with a supplier that creates new instances and then those instances are modified by the modifications specified with the with method.

The GenericBuilder would be used for Person like this:

Person value = GenericBuilder.of(Person::new)
            .with(Person::setName, "Otto").with(Person::setAge, 5).build();

Properties and further Usages

But there is more about that builder to discover.

For example, the above implementation clears the modifiers. This could be moved into its own method. Therefore, the builder would keep its state between modifications and it would be easy create multiple equal instances. Or, depending on the nature of an instanceModifier, a list of varying objects. For example, an instanceModifier could read its value from an increasing counter.

Continuing with this thought, we could implement a fork method that would return a new clone of the GenericBuilder instance that it is called on. This is easily possible because the state of the builder is just the instantiator and the list of instanceModifiers. From there on, both builders could be altered with some other instanceModifiers. They would share the same basis and have some additional state set on built instances.

The last point I consider especially helpful when needing heavy entities for unit or even integration tests in enterprise applications. There would be no god-object for entities, but for builders instead.

The GenericBuilder can also replace the need for different test value factories. In my current project, there are many factories used for creating test instances. The code is tightly coupled to different test scenarios and it is difficult to extract portions of a test factory for reuse in another test factory in a slightly different scenario. With the GenericBuilder, reusing this becomes much easier as there is only a specific list of instanceModifiers.

To verify that created instances are valid, the GenericBuilder could be initialized with a set of predicates, which are verified in the build method after all instanceModifiers are run.

public T build() {
    T value = instantiator.get();
    instanceModifiers.forEach(modifier -> modifier.accept(value));
    verifyPredicates(value);
    instanceModifiers.clear();
    return value;
}

private void verifyPredicates(T value) {
    List<Predicate<T>> violated = predicates.stream()
            .filter(e -> !e.test(value)).collect(Collectors.toList());
    if (!violated.isEmpty()) {
        throw new IllegalStateException(value.toString()
                + " violates predicates " + violated);
    }
}

Immutable object creation

To use the above scheme for the creation of immutable objects, extract the state of the immutable object into a mutable object and use the instantiator and builder to operate on the mutable state object. Then, add a function that will create a new immutable instance for the mutable state. However, this requires that the immutable object either has its state encapsulated like this or it be changed in that fashion (basically applying parameter object pattern to its constructor).

This is in some way different than a builder was used in pre-java-8 times. There, the builder itself was the mutable object that created a new instance at the end. Now, we have a separation of the state a builder keeps in a mutable object and the builder functionality itself.

In essence
Stop writing boilerplate builder patterns and get productive using the GenericBuilder.

riddle_me_this
  • 8,575
  • 10
  • 55
  • 80
SpaceTrucker
  • 13,377
  • 6
  • 60
  • 99
  • 6
    Why would you want to use this instead of the shorter and much more straightforward: `Person value = new Person(); person.setName("Otto"); persion.setAge(5);` – Jesper Jul 31 '15 at 20:43
  • 1
    @Jesper because I would like to create similiar objects for unit tests for example. I will explain a bit more. – SpaceTrucker Jul 31 '15 at 20:45
  • What this solution sadly lacks is the abstraction level you can put in hand-coded builders. For example if you're building something like an `OptionalInt`, where you have a boolean field to signal whether a value is present or not, and an `int` field to store the value (if present), your builder can't enforce those invariants. (Though if you've got separate public setters for them, that's a problem in itself.) – biziclop Jul 31 '15 at 20:53
  • 1
    Yes, I can see that a builder would be useful if you'd want to build multiple objects with (partly) the same values. – Jesper Jul 31 '15 at 20:53
  • 12
    But a builder is usually a mutable object used to create an immutable one (e.g. `StringBuilder` and `String`). This can't be used to create an immutable object because it requires setters. Since your `Person` class is mutable, there's no significant difference between a `Person` and a `GenericBuilder`. If you need a to create many similar `Person` objects, all you need is a copy constructor. – Paul Boddington Jul 31 '15 at 20:53
  • @pbabcdefp [Wikipedia](https://en.wikipedia.org/wiki/Builder_pattern) has a different, more general perspective on that: "Instead of using numerous constructors, the builder pattern uses another object, a builder, that receives each initialization parameter step by step and then returns the resulting constructed object at once." So the focus is really on initializing a complex object requiring many initialization parameters. – SpaceTrucker Jul 31 '15 at 21:10
  • @pbabcdefp I have added a section addressing the creation of immutable objects. – SpaceTrucker Jul 31 '15 at 21:16
  • 1
    I'll admit I like the look of `GenericBuilder.of(Person::new).with(Person::setName, "Otto").with(Person::setAge, 5).build();`, but I don't think you're really addressing the point that a `Person` **is** a builder (or it could be with a copy constructor). When you say, "extract the state of the immutable object into a mutable object", that's what I already do - the mutable object is called a builder object. – Paul Boddington Jul 31 '15 at 21:22
  • 1
    @pbabcdefp you are correct with your last sentence, but since you now have a generic builder you can strip of the builder logic from your mutable object and make it a pojo. – SpaceTrucker Aug 01 '15 at 10:07
  • @ErwinDupont I rejected your edit, because this seems rather different from mine from an implementation standpoint. Please add an own answer to this question stating how using an interface instead of the class would be different from mine. however very interesting solution! – SpaceTrucker Feb 16 '16 at 12:42
  • `Person::setName` means that the setters are static?!! – user3399000 Jun 24 '17 at 11:34
  • @user3399000 No, it means that a [BiConsumer](https://docs.oracle.com/javase/8/docs/api/java/util/function/BiConsumer.html) is inferred from the method reference where the first argument is the instance on which the setter is to be invoked and the second argument is the argument to the setter. – SpaceTrucker Jun 26 '17 at 06:37
  • 1
    @JarrodRoberson How does this violate type safety? What do you mean with preconditions (maybe you did confuse with postconditions)? How is the order of inputs changed when the `instanceModifiers` are stored as list and applied in order in which they were added to the `GenericBuilder`? – SpaceTrucker Sep 08 '17 at 17:35
  • I like the idea, but the objects for which you use builders aren't supposed to have setters, otherwise they'd still mutable, so `Person::setName` etc. shouldn't even exist. Sujit Kamthe's answer looks more elegant to me. – Jelle Blaauw Jul 29 '19 at 10:52
10
public class PersonBuilder {
    public String salutation;
    public String firstName;
    public String middleName;
    public String lastName;
    public String suffix;
    public Address address;
    public boolean isFemale;
    public boolean isEmployed;
    public boolean isHomewOwner;

    public PersonBuilder with(
        Consumer<PersonBuilder> builderFunction) {
        builderFunction.accept(this);
        return this;
    }


    public Person createPerson() {
        return new Person(salutation, firstName, middleName,
                lastName, suffix, address, isFemale,
                isEmployed, isHomewOwner);
    }
}

Usage

Person person = new PersonBuilder()
    .with($ -> {
        $.salutation = "Mr.";
        $.firstName = "John";
        $.lastName = "Doe";
        $.isFemale = false;
    })
    .with($ -> $.isHomewOwner = true)
    .with($ -> {
        $.address =
            new PersonBuilder.AddressBuilder()
                .with($_address -> {
                    $_address.city = "Pune";
                    $_address.state = "MH";
                    $_address.pin = "411001";
                }).createAddress();
    })
    .createPerson();

Refer: https://medium.com/beingprofessional/think-functional-advanced-builder-pattern-using-lambda-284714b85ed5

Disclaimer: I am the author of the post

Sujit Kamthe
  • 811
  • 11
  • 14
9

You can check the lombok project

For your case

@Builder
public class Person {
    private String name;
    private int age;
}

It would generate the code on the fly

public class Person {
    private String name;
    private int age;
    public String getName(){...}
    public void setName(String name){...}
    public int getAge(){...}
    public void setAge(int age){...}
    public Person.Builder builder() {...}

    public static class Builder {
         public Builder withName(String name){...}
         public Builder withAge(int age){...}
         public Person build(){...}
    }        
}

Lombok do it on the compilation phase and is transparent for developers.

popcorny
  • 1,710
  • 16
  • 16
  • 2
    Although this is also a correct builder implementation in java 8, the question was specifically ment to get information on how to use features new to java 8 for the implementation of the builder pattern. – SpaceTrucker Aug 02 '15 at 12:47
  • Yes. You are right. I missed the requirement of using java8 features. Here is an builder example in my github - [BuilderMonad](https://github.com/popcornylu/BuilderMonad), in which i used java8 lambda to implement a builder monad. The way to implement is very similar to your answer :) – popcorny Aug 02 '15 at 23:12
  • Downvoted because Lombok and e.g. Jackson does not play well together. Stay clear of Lombok! See e.g. https://stackoverflow.com/questions/39381474/cant-make-jackson-and-lombok-work-together – Jonas G. Drange Oct 11 '18 at 10:50
5

We can use Consumer functional interface of Java 8 to avoid multiple getter/setter methods.

Refer the below-updated code with Consumer interface.

import java.util.function.Consumer;

public class Person {

    private String name;

    private int age;

    public Person(Builder Builder) {
        this.name = Builder.name;
        this.age = Builder.age;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Person{");
        sb.append("name='").append(name).append('\'');
        sb.append(", age=").append(age);
        sb.append('}');
        return sb.toString();
    }

    public static class Builder {

        public String name;
        public int age;

        public Builder with(Consumer<Builder> function) {
            function.accept(this);
            return this;
        }

        public Person build() {
            return new Person(this);
        }
    }

    public static void main(String[] args) {
        Person user = new Person.Builder().with(userData -> {
            userData.name = "test";
            userData.age = 77;
        }).build();
        System.out.println(user);
    }
}

Refer the below link to know the detailed information with the different examples.

https://medium.com/beingprofessional/think-functional-advanced-builder-pattern-using-lambda-284714b85ed5

https://dkbalachandar.wordpress.com/2017/08/31/java-8-builder-pattern-with-consumer-interface/

António Ribeiro
  • 4,129
  • 5
  • 32
  • 49
Balachandar
  • 392
  • 5
  • 8
4

Building upon this answer, here's a quasi-immutable version of the builder pattern:

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * Responsible for constructing objects that would otherwise require
 * a long list of constructor parameters.
 *
 * @param <MT> The mutable definition for the type of object to build.
 * @param <IT> The immutable definition for the type of object to build.
 */
public class GenericBuilder<MT, IT> {
  /**
   * Provides the methods to use for setting object properties.
   */
  private final Supplier<MT> mMutable;

  /**
   * Calling {@link #build()} will instantiate the immutable instance using
   * the mutator.
   */
  private final Function<MT, IT> mImmutable;

  /**
   * Adds a modifier to call when building an instance.
   */
  private final List<Consumer<MT>> mModifiers = new ArrayList<>();

  /**
   * Constructs a new builder instance that is capable of populating values for
   * any type of object.
   *
   * @param mutator Provides methods to use for setting object properties.
   */
  protected GenericBuilder(
      final Supplier<MT> mutator, final Function<MT, IT> immutable ) {
    mMutable = mutator;
    mImmutable = immutable;
  }

  /**
   * Starting point for building an instance of a particular class.
   *
   * @param supplier Returns the instance to build.
   * @param <MT>     The type of class to build.
   * @return A new {@link GenericBuilder} capable of populating data for an
   * instance of the class provided by the {@link Supplier}.
   */
  public static <MT, IT> GenericBuilder<MT, IT> of(
      final Supplier<MT> supplier, final Function<MT, IT> immutable ) {
    return new GenericBuilder<>( supplier, immutable );
  }

  /**
   * Registers a new value with the builder.
   *
   * @param consumer Accepts a value to be set upon the built object.
   * @param value    The value to use when building.
   * @param <V>      The type of value used when building.
   * @return This {@link GenericBuilder} instance.
   */
  public <V> GenericBuilder<MT, IT> with(
      final BiConsumer<MT, V> consumer, final V value ) {
    mModifiers.add( instance -> consumer.accept( instance, value ) );
    return this;
  }

  /**
   * Instantiates then populates the immutable object to build.
   *
   * @return The newly built object.
   */
  public IT build() {
    final var value = mMutable.get();
    mModifiers.forEach( modifier -> modifier.accept( value ) );
    mModifiers.clear();
    return mImmutable.apply( value );
  }
}

Example usage:

final var caret = CaretPosition
    .builder()
    .with( CaretPosition.Mutator::setParagraph, 5 )
    .with( CaretPosition.Mutator::setMaxParagraph, 10 )
    .build();

When the mutator's reference is released, the state of the returned object is effectively immutable. The CaretPosition class resembles:

public class CaretPosition {
  public static GenericBuilder<CaretPosition.Mutator, CaretPosition> builder() {
    return GenericBuilder.of( CaretPosition.Mutator::new, CaretPosition::new );
  }

  public static class Mutator {
    private int mParagraph;
    private int mMaxParagraph;

    public void setParagraph( final int paragraph ) {
      mParagraph = paragraph;
    }

    public void setMaxParagraph( final int maxParagraph ) {
      mMaxParagraph = maxParagraph;
    }
  }

  private final Mutator mMutator;
  
  private CaretPosition( final Mutator mutator ) {
    mMutator = mutator;
  }

  // ...

From here, the CaretPosition can freely reference its internal Mutator instance, which handily provides the opportunity to avoid violating encapsulation by otherwise exposing get accessor methods on the immutable class without necessity.

This is only quasi-immutable because the values can be changed if a handle to the mutable instance is retained. Here's how immutability can be violated:

final var mutable = CaretPosition.builder()
    .with( CaretPosition.Mutator::setParagraph, 5 )
    .with( CaretPosition.Mutator::setMaxParagraph, 10 );
final var caret = mutable.build();
mutable.setParagraph( 17 );
System.out.println( "caret para: " + caret.toString() );

Should caret.toString() include the paragraph value, the resulting string will contain the value 17 instead of 5, thereby violating immutability. Another downside to this approach is that if validation is performed at build() time, the second call to setParagraph will not be passed through the validator.

Ways to avoid this include:

  • Immutable copy constructor. Copy the mutable member variables into the immutable instance, which entails duplicating all member variables.
  • Mutator copy constructor. Copy the Mutator into a new object reference, which avoids duplicating all member variables while building a truly immutable instance of the desired type.
  • Clone. Clone the mutator when constructing the immutable instance, which requires either implementing Serializable everywhere or using a deep-copy library.
  • Library. Scrap this solution for Project Lombok, AutoValue, or Immutables.

The Mutator copy constructor option would resemble:

private Mutator() {
}

private Mutator( final Mutator mutator) {
  mParagraph = mutator.mParagraph;
  mMaxParagraph = mutator.mMaxParagraph;
}

Then the change to CaretPosition is trivial---instantiate the Mutator using its copy constructor:

private CaretPosition( final Mutator mutator ) {
  mMutator = new Mutator( mutator );
}
Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
3

I have recently tried to revisit the builder pattern in Java 8, and I am currently using the following approach:

public class Person {

    static public Person create(Consumer<PersonBuilder> buildingFunction) {
        return new Person().build(buildingFunction);
    }

    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    private Person() {

    }

    private Person build(Consumer<PersonBuilder> buildingFunction) {
        buildingFunction.accept(new PersonBuilder() {

            @Override
            public PersonBuilder withName(String name) {
                Person.this.name = name;
                return this;
            }

            @Override
            public PersonBuilder withAge(int age) {
                Person.this.age = age;
                return this;
            }
        });

        if (name == null || name.isEmpty()) {
            throw new IllegalStateException("the name must not be null or empty");
        }

        if (age <= 0) {
            throw new IllegalStateException("the age must be > 0");
        }

        // check other invariants

        return this;
    }
}

public interface PersonBuilder {

    PersonBuilder withName(String name);

    PersonBuilder withAge(int age);
}

Usage:

var person = Person.create(
    personBuilder -> personBuilder.withName("John Smith").withAge(43)
);

Advantages:

  • A clean builder interface
  • Little to no boilerplate code
  • The builder is well encapsulated
  • It's easy to segregate the optional attributes from the mandatory attributes of the target class (the optional attributes are specified in the builder)
  • No setter needed in the target class (in DDD, you generally don't want setters)
  • Use of a static factory method to create an instance of the target class (instead of using the new keyword, so it's possible to have several static factory methods, each with a meaningful name)

Possible drawbacks:

  • The calling code can save a reference to the passed-in builder and later screw up the mounted instance, but who will do that?
  • If the calling code saves a reference to the passed-in builder, a memory leak can occur

Possible alternative:

We can setup a constructor with a building function, as follows:

public class Person {

    static public Person create(Consumer<PersonBuilder> buildingFunction) {
        return new Person(buildingFunction);
    }

    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    private Person(Consumer<PersonBuilder> buildingFunction) {
        buildingFunction.accept(new PersonBuilder() {

            @Override
            public PersonBuilder withName(String name) {
                Person.this.name = name;
                return this;
            }

            @Override
            public PersonBuilder withAge(int age) {
                Person.this.age = age;
                return this;
            }
        });

        if (name == null || name.isEmpty()) {
            throw new IllegalStateException("the name must not be null or empty");
        }

        if (age <= 0) {
            throw new IllegalStateException("the age must be > 0");
        }

        // check other invariants
    }
}
Stéphane Appercel
  • 1,427
  • 2
  • 13
  • 21