Java 8 – Builder pattern with Consumer interface

In this post, I am going to provide an example to show how we can implement the Builder pattern with Consumer interface.

With the use of Consumer interface, we can get rid of some boilerplate code in Java. Assume that you have a model class “Book” which contains a builder class inside of it. So to create an instance of “Book”, We can make use of this builder class. See the below example to know how we can do it in a normal way.


import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Book {

    private String title;
    private List authors;
    private float price;

    public Book(Builder builder) {
        this.title = builder.title;
        this.authors = builder.authors;
        this.price = builder.price;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Builder{");
        sb.append("title='").append(title).append('\'');
        sb.append(", authors=").append(authors);
        sb.append(", price=").append(price);
        sb.append('}');
        return sb.toString();
    }

    public static class Builder {

        private String title;
        private List authors;
        private float price;

        public Builder withTitle(String title){
            this.title=title;
            return this;
        }
        public Builder withAuthors(List authors){
            this.authors=authors;
            return this;
        }
        public Builder withPrice(float price){
            this.price=price;
            return this;
        }
        public Book build() {
            return new Book(this);
        }
    }

    public static void main(String[] args) {
        Book book = new Builder().withTitle("Design Patterns: Elements of Reusable Object-Oriented Software").
                withAuthors(new ArrayList(Arrays.asList(new String[]{"Erich Gamma", "John Vlissides", "Ralph Johnson", "Richard Helm"}))).
                withPrice(16.98f).build();
        System.out.println(book);
    }
}

If you look at the above example, then you can understand there are two many setter methods available in the Builder class. We can avoid this with Java 8 Consumer interface. I have changed the above example class with Consumer interface.

Refer below the code.


import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

public class Book {

    private String title;
    private List authors;
    private float price;

    public Book(Builder builder) {
        this.title = builder.title;
        this.authors = builder.authors;
        this.price = builder.price;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Builder{");
        sb.append("title='").append(title).append('\'');
        sb.append(", authors=").append(authors);
        sb.append(", price=").append(price);
        sb.append('}');
        return sb.toString();
    }

    public static class Builder {

        public String title;
        private List authors;
        public float price;

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

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

    public static void main(String[] args) {
        Book book = new Book.Builder().with(bookData -> {
            bookData.title = "Design Patterns: Elements of Reusable Object-Oriented Software";
            bookData.authors = new ArrayList(
                    Arrays.asList(new String[]{"Erich Gamma", "John Vlissides", "Ralph Johnson", "Richard Helm"}));
            bookData.price = 16.98f;
        }).build();
        System.out.println(book);
    }
}

In the above code, I have removed all the setter methods available in the Builder class and created only one method with() which takes the Consumer function as input.

Consumer is a functional interface in Java and it accepts a single input argument and does not return any value. Here we are going to pass a Consumer object with a value for each field(Builder class).

We can also update the reference name ‘bookData’ with any symbol. Refer the below example where I have used the symbol ‘$’ to make it simpler.


  public static void main(String[] args) {
        Book book = new Book.Builder().with($ -> {
            $.title = "Design Patterns: Elements of Reusable Object-Oriented Software";
            $.authors = new ArrayList(
                    Arrays.asList(new String[]{"Erich Gamma", "John Vlissides", "Ralph Johnson", "Richard Helm"}));
            $.price = 16.98f;
        }).build();
        System.out.println(book);
    } 

The one drawback of this approach is to update all the fields of the Builder class as public. But this should be fine as we are not going to modify the actual class (Book) here.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s