Builder Design Pattern

Design Pattern Tutorial Series

Builder Design Pattern in Java - 5-Step Guide

The Builder Pattern helps separating the construction of complex objects from the context where they are needed. Additionally, the pattern also contains different variations of objects which have a common construction procedure.

This makes the builder pattern convenient to use when complex objects needs to be created and well aligned with the Single Responsibility Principle. (SRP)

Given that the builder pattern sets up a method for constructing new objects, it is understandably categorized as a creational design pattern.

Table of Contents

INTEGU - builder-design-pattern-overview

Step 1 - The Objective

For this tutorial we will set the objective of making a burger restaurant, which can make different variations of burgers.

The only thing we know is that all burgers share the same structure and contain the same five basic elements in some form. The five elements being: bun, meat, salad, cheese and sauce.

However, each of the elements should be seen as a complex process (e.g. making the secret sauce) and it would therefore be desirable to have these processes hidden away when making different variations of burgers.

Secondly it would also be preferable to have defined instructions for how to build each of the different variations of burgers (e.g. cheeseburger), so that we do not have to provide all the ingredients each time when we are making a burger.

Before we investigate how the builder design pattern will be able to solve these issues, we will start by seeing how this system could be build based on construction of the burger object in the main context.

INTEGU - builder-objective

Step 2 – Construction In The Main Context

The basic problem that we want to solve is to have a way of making burger objects with the possibility of making different variations in each of the five elements which all burgers consist of.

The simplest way of achieving this is to create a POJO (Plain Old Java Object – Link), which represent the basic functions and fields of a burger object.

This solves the basic objective of being able to create any variation of burgers on the menu card (e.g. cheeseburger), but it does not hide the processes away or define instructions for how to repeatably build the same burger object simply.

Constructing the burger object in the main context, as can be seen below, is neither the builder pattern nor clean code.

Let’s try and see how the builder pattern can solve the same objective, and at the same time handle the additional objectives.

builder-disadvantages - Design Pattern - INTEGU
public class Main {
    public static void main(String[] args) {
        Burger cheeseBurger = new Burger();
        cheeseBurger.setBun("White Bread");
        cheeseBurger.setMeat("Beef");
        cheeseBurger.setSalad("Iceberg");
        cheeseBurger.setCheese("American Chesse");
        cheeseBurger.setSauce("Secret Sauce");
        cheeseBurger.print();
    }

Step 3 - Concept Of The Builder Pattern

When talking about the builder design pattern, it is important to understand the concept of the Director and the Builder.

The director’s job is to invoke the building process of the builder. The builder’s job is to manage the different building procedures associated with each of the different variations of objects, in this case the burgers.

The builder pattern consists of two classes, a sub- and super class.

The super class is an abstract class which only declares the concrete methods and fields which all sub class have in common. In this case that would be the print method and the burger object field. Additionally, the super class also states the expected process methods that any builder sub class should implement.

The sub class, then implements the declared process methods, and uses the super classes’ burger object field to set the five different elements to what is expected from that specific sub class builder.

Continuing this pattern makes it possible to create multiple different builder classes, which all know exactly how to build their specific variation of burger, meanwhile still following the common rules of the builder super class.

By invoking the builder through the director, it is thereby possible to swap between builder instances, which is what makes the builder pattern both flexible and practical.

Utilizing the builder design pattern solves the main objective as well as hide the process method and clearly defines how each of the burger builders should work.

That all sounds great! So, let’s dive into the class diagram and code to see how we actually implement the builder pattern.

INTEGU - builder-advantages

Step 4 - Class Diagram (UML)

As we learn from the previous section the builder pattern consists of two main class types: the builder and the director. Seen from the context of the program which will be using the builder pattern, we actually only want to be interacting with the director.

The director will ensure that we build the correct burger object based on which builder we provide it with. This is because inside the director, we set its builder object field (setBuilder-method) and afterwards ask it to build the object based on the provided builder (build-method).

As previously explain the different builder class all have different variations in terms of the five elements which a burger consists of. But when it comes to everything else, they are all alike since they extend the same super class.

INTEGU - builder-class-diagram

Step 5 - Code (Java)

Given that code is sometimes difficult to learn through concepts and theory, I provide the code for the example.

Hopefully, this will be sufficient for you to learn to identify and implement the builder pattern in your future endeavors as a software developer.

Object

public class Burger {
    private String bun = "No Bun";
    private String meat = "No Meat";
    private String salad = "No Salad";
    private String cheese = "No Cheese";
    private String sauce = "No Sauce";
    public void setBun(String bun) {
        this.bun = bun;
    }
    public void setMeat(String meat) {
        this.meat = meat;
    }
    public void setSalad(String salad) {
        this.salad = salad;
    }
    public void setCheese(String cheese) {
        this.cheese = cheese;
    }
    public void setSauce(String sauce) {
        this.sauce = sauce;
    }
    public void print() {
        System.out.println(
                "Burger is finished! " + "n" +
                "Bun: " + bun +
                " - Meat: " + meat +
                " - Cheese: " + cheese +
                " - Salad: " + salad +
                " - Sauce: " + sauce);
    }
}

Builder

public abstract class BurgerBuilder {
    Burger burger = new Burger();
    abstract void buildBun();
    abstract void buildMeat();
    abstract void buildSalad();
    abstract void buildCheese();
    abstract void buildSauce();
    Burger build(){
        return burger;
    };
}
public class CheeseBurgerBuilder extends BurgerBuilder {
    @Override
    public void buildBun() {
        burger.setBun("White Bread");
    }
    @Override
    public void buildMeat() {
        burger.setMeat("Beef");
    }
    @Override
    public void buildSalad() {
        burger.setSalad("Iceberg");
    }
    @Override
    public void buildCheese() {
        burger.setCheese("American Cheese");
    }
    @Override
    public void buildSauce() {
        burger.setSauce("Secret Sauce");
    }
}

Director

public class BurgerResuturant {
    private BurgerBuilder burgerBuilder;
    public void setBuilder(BurgerBuilder burgerBuilder) {
        this.burgerBuilder = burgerBuilder;
    }
    public Burger buildBurger(){
        burgerBuilder.buildBun();
        burgerBuilder.buildMeat();
        burgerBuilder.buildSalad();
        burgerBuilder.buildCheese();
        burgerBuilder.buildSauce();
        return burgerBuilder.build();
    }
}

How To Use The Builder Pattern

public class Main {
    public static void main(String[] args) {
        BurgerResuturant burgerResuturant = new BurgerResuturant();
        burgerResuturant.setBuilder(new CheeseBurgerBuidler());
        buildBurger(burgerResuturant);
        burgerResuturant.setBuilder(new VeganBurgerBuidler());
        buildBurger(burgerResuturant);
    }
    private static void buildBurger(BurgerResuturant burgerResuturant) {
        Burger burger = burgerResuturant.buildBurger();
        burger.print();
    }
}

Recommended Reading

Article

  • Theoretical walkthrough – Source Making – Blog
  • Practical walkthrough – Tutorialspoint – Blog
  • Practical walkthrough – Springframework Guru – Blog
  •  

Tools

  • Camtasia – Used for illustrations – Camtasia

Books

Video Tutorial 

  • Practical – Derek Banas – YouTube

About

Hi, I'm the Author

My name is Daniel H. Jacobsen and I’m a dedicated and highly motivated software developer with a masters engineering degree within the field of ICT. 

I have through many years of constantly learning and adapting to new challenges, gained a well-rounded understanding of what it takes to stay up to date with new technologies, tools and utilities. 

The purpose of this blog is to share both my learnings and knowledge with other likeminded developers as well as illustrating how these topics can be taught in a different and alternative manner.

If you like the idea of that, I would encourage you to sign up for the newsletter.

Cheers! 🍺

Didn't Find What You Were Looking For?

Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages
Scroll to Top