builder pattern image

Builder Pattern

In Object-Oriented Programming (OOP) we spend most of our time creating objects or instances of classes. We usually prefer constructors when we create these objects. However, increasing the number of fields leads us to think differently because of the exponential and unnecessary complexity of the constructors. Builder pattern, on the other hand, overcomes this issue by using a builder to create an object in a step by step manner.

Creating objects is a commonly occurring task and we should find a reusable solution about creating them.

And when you apply a reusable solution for that kind of commonly occurring task we call it a “design pattern“.

Design patterns are very well known in software engineering, and I will not get into the details, however, I want to mention about “Builder Pattern” which is one of the “Creational Patterns”.

Builder pattern allows you to construct a complex object in a step by step process.

Normally, you’d have to write different constructors for every single combination of your mandatory fields of your class.

On the other hand, by using builder pattern you will set your parameters one by one in a meaningful way.

The catch is builder pattern comes with its burden of code duplication though. However, you will gain much more elegant and readable code. To be able to achieve that we have to follow the fluent interfaces. You can find more detail about Fluent Interface by Martin Fowler to gain more knowledge about fluent interfaces later on.

Let’s see the builder pattern in action when you want to make a pizza

package com.gunhansancar.android.pizza.builder;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Günhan on 10.03.2016.
*/
public class Pizza {
private final List<Topping> toppings;
private final String size;
private final String souce;
private Pizza(Builder builder) {
this.size = builder.size;
this.souce = builder.souce;
this.toppings = builder.toppings;
}
public String description() {
StringBuilder sb = new StringBuilder();
sb.append(String.format("This is a %s sized pizza with %s souce.", size, souce));
if (toppings != null) {
sb.append("\nIt contains;");
for (Topping topping : toppings) {
sb.append(String.format("\n%s", topping.description()));
}
}
return sb.toString();
}
private static class Topping {
private final String name;
private boolean cooked;
public Topping(String name) {
this.name = name;
}
public void setCooked(boolean cooked) {
this.cooked = cooked;
}
public String description() {
return String.format("%s%s", cooked ? "Cooked " : "", name);
}
}
public static class Builder {
private List<Topping> toppings;
private String size;
private String souce;
public Builder(String size) {
this.size = size;
}
public Builder with(String name) {
if (toppings == null) {
toppings = new ArrayList<>();
}
toppings.add(new Topping(name));
return this;
}
public Builder cooked() {
if (toppings != null && toppings.size() > 0) {
Topping topping = toppings.get(toppings.size() – 1);
topping.setCooked(true);
}
return this;
}
public Builder souce(String souce) {
this.souce = souce;
return this;
}
public Pizza build() {
return new Pizza(this);
}
}
public static void main(String[] args) {
Pizza pizza = new Pizza.Builder("medium")
.souce("tomato")
.with("Onion")
.with("Sausage").cooked()
.with("Mozzarella")
.build();
System.out.println(pizza.description());
}
}
view raw Pizza.java hosted with ❤ by GitHub

The most noticeable part is the style of the building the pizza object. The intention behind the building code is easily understandable.

Pizza pizza = new Pizza.Builder("medium")
        .souce("tomato")
        .with("Onion")
        .with("Sausage").cooked()
        .with("Mozzarella")
        .build();

Fluency in this code is not only about the chaining of the calls, but also it smoothly allows you to set the properties of the object.

and finally the output is:

This is a medium sized pizza with tomato souce.
 It contains;
 Onion
 Cooked Sausage
 Mozzarella

The basics of the builder pattern

Common ımplementatıon detaıls
  1. The methods of builder class always return itself.
  2. Make the constructors private so it cannot be instantiated by any other means.
  3. Make the fields finals so that your object becomes immutable after it’s being created.
  4. Do not try to apply this pattern if you do not need it. For instance, consider builder pattern when you have 5 or more fields in your constructors.
  5. There are some plugins for common IDE’s to save you from the boilerplate code writings.
Advantages
  1. Allows method chaining and fluency in your code.
  2. Cleaner and more readable code.
  3. Allows you to construct immutable objects by using mutable fields of builder.
  4. You can force users to call some methods to build and actual object.
Disadvantages
  1. Code duplication. Since you need to carry out all of the fields from the original class.

Real world examples

Android Dialog Builder is a great example for builder pattern.

AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(title)
        .setMessage(message)
        .setCancelable(cancelable)
        .setPositiveButton(positive, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                if (action != null) {
                    action.click();
                }
            }
        });

if (negative != null) {
    builder.setNegativeButton(negative, new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int id) {
            dialog.cancel();
            if (action != null) {
                action.cancel();
            }
        }
    });
}

AlertDialog alertDialog = builder.create();
alertDialog.show();

Thank you.

Related posts


Posted

in

by

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *