Builder

Design patterns

  |   10 min read

Purpose

Builder (creational pattern) separates the way of creationg object from their representation. Divides the object’s manufacturing process into stages. Each of the stages can be implemented in many ways, which allows creating different representations of objects of the same class. thanks to granting the principle of SRP - single responsibility, OCP - open / closed and DIP - reversal of dependencies the change the way of creating objects is flexible and has a low cost. In addition, the code is easier to test, maintain, handle exceptions and prevent duplication. This pattern works on a similar basis to the construction plan. Director as a responsible for the entire construction commissions to perform a specific job for a specific Builder. As a result of the combination of their work, the final Product is created. Speaking of the Builder pattern, it is impossible to not mention its variation of the so-called Fluent Builder. Its role is to create objects (with a lot of parameters) in a clear way by replacing the constructor.

Limitations

Using a pattern forces to create a specific Builder for each type of Product. In addition, there is no guarantee of initialization of the class fields and Dependency Injection may be difficult.

Usage

Builder is used in the implementation of complex objects (composites), which can be built in various ways, and their initialization is a multi-stage process. The use of the pattern avoids the creation of super class with extensive responsibility. It is used in initializing composites in many external and internal libraries. In situations where the class builder has a long list of parameters (about 5), consider using Fluent Builder.

Implementation

In the classic version of the pattern, object of Director class orders the product (Product) to the specific builder (ConcreteBuilder) overseeing its work. Director stores references to Builder and during building the product, invokes individual operations on it. Each builder implements the common behavior of an abstract Builder class or interface, creating a product in its own specific way. Builder contains a reference to the product and returns it to the supervisor at the end of the work.

Builder diagram

The following listing shows the implementations of the classic form Builder.

public class Product {

    private String part1;
    private String part2;

    public void setPart1(String part1) {
    	this.part1 = part1;
    }

    public void setPart2(String part2) {
    	this.part2 = part2;
    }
}

public abstract class Builder {

    protected Product product;

    public Product getProduct() {
    	return product;
    }

    public abstract void buildPart1();
    public abstract void buildPart2();
}

public class ConcreteBuilder extends Builder {

    public void buildPart1() {
    	product.setPart1("oval");
    }

    public void buildPart2() {
    	product.setPart2("green");
    }
}

public class Director {

    private Builder builder;

    public Director(Builder builder) {
	    this.builder = builder;
    }

    public Product build() {
        builder.buildPart1();
        builder.buildPart2();
        return builder.getProduct();
    }
}

The client uses the implemented pattern in the following manner.

Builder concreteBuilder = new ConcreteBuilder();
Director director = new Director(concreteBuilder);
Product product = director.build();

In the Fluent Builder version, the instance of Product class is created by calling the Builder static class. Using this way of creating objects, the initialization of all required fields and its order should be provided. The following listing shows the basic implementations of the pattern in the Fluent Builder variant.

public class Product {

    private String part1;
    private String part2;

    private Product(Builder builder) {
        this.part1 = builder.part1;
        this.part2 = builder.part2;
    }

    public static class Builder {

        private String part1;
        private String part2;

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

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

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

Thanks to such this approach, instead of creating an object by calling its constructor, its builder can be used.

Product product = new Product.Builder()
        .part1("oval")
        .part2("green")
        .build();

Example

The application assists the staff of gastronomy chain in receiving and issuing orders. The Staff service worker selects on the screen the dinner menus defined by the customer Meal. The cooks Cook with specializations of creating specific dish, receives list of orders to be processed. After preparing the meal, he passes it to service, and from there it goes to the customer. The following listing shows how to place orders using the Builder.

public class Meal {

    private Burger burger;
    private Beverage beverage;
    private Extra extra;

    public void addBurger(Burger burger) {
        this.burger = burger;
    }

    public void addBeverage(Beverage beverage) {
        this.beverage = beverage;
    }

    public void addExtra(Extra extra) {
        this.extra = extra;
    }
}

public abstract class Cook {

    protected Meal meal;

    public Meal getMeal() {
        return meal;
    }

    public abstract void prepareBurger();
    public abstract void prepareBeverage();
    public abstract void prepareExtra();
}

public class BigMeal extends Cook {

    @Override
    public void prepareBurger() {
        meal.addBurger(new Burger("Bacon Burger"));
    }

    @Override
    public void prepareBeverage() {
        meal.addBeverage(new Beverage("Lager Beer"));
    }

    @Override
    public void prepareExtra() {
        meal.addExtra(new Extra("French fries"));
    }
}

public class KidsMeal extends Cook {

    @Override
    public void prepareBurger() {
        meal.addBurger(new Burger("Cheeseburger"));
    }

    @Override
    public void prepareBeverage() {
        meal.addBeverage(new Beverage("Orange Juice"));
    }

    @Override
    public void prepareExtra() {
        meal.addExtra(new Extra("Apple"));
    }
}

public class Staff {

    private Cook builder;

    public Staff(Cook builder) {
        this.builder = builder;
    }

    public Meal makeMeal() {
        builder.prepareBurger();
        builder.prepareBeverage();
        builder.prepareExtra();
        return builder.getMeal();
    }
}

In addition, the customer can compose his own set. For this purpose, the service worker selects the products manually. The following listing illustrates the implementation of manual creation of a set using Fluent Builder.

public class SpecificMeal {

    private Burger burger;
    private Beverage beverage;
    private Extra extra;

    private SpecificMeal(Chef chef) {
        this.burger = chef.burger;
        this.beverage = chef.beverage;
        this.extra = chef.extra;
    }

    public static class Chef {

        private Burger burger;
        private Beverage beverage;
        private Extra extra;

        public Chef prepareBurger(Burger burger) {
            this.burger = burger;
            return this;
        }

        public Chef prepareBeverage(Beverage beverage) {
            this.beverage = beverage;
            return this;
        }

        public Chef prepareExtra(Extra extra) {
            this.extra = extra;
            return this;
        }

        public SpecificMeal makeMeal() {
            return new SpecificMeal(this);
        }
    }
}

The selection process for sets is as follows.

Staff john = new Staff(new BigMeal());
Meal definedMeal = john.makeMeal();
SpecificMeal specificMeal = new SpecificMeal.Chef()
        .prepareBurger(new Burger("Double Cheesburger"))
        .prepareBeverage(new Beverage("Cola"))
        .prepareExtra(new Extra("Onion Rings"))
        .makeMeal();
//give meals to client

Libraries

Many external libraries such as: Retrofit or system elements, eg: AlertDialog, Notification use the Builder. However, to implement pattern there is no external libraries.