loading...

Builder Design Pattern

nishparadox profile image Nishan Pantha ・2 min read

Main Points

  • use builder design pattern when the constructor is very much populated by arguments.
  • it becomes tedious for creating new instances of a class if constructor is accepting so much arguments.

example

constructor over population

public class Burger {
    private int size;

    private boolean cheese = false;
    private boolean tomato = false;
    private boolean lettuce = false;
    private boolean pepperoni = false;

    public Burger(int size, boolean cheese, boolean tomato, boolean lettuce, boolean pepperoni) {
        this.size = size;
        this.cheese = cheese;
        this.tomato = tomato;
        this.lettuce = lettuce;
        this.pepperoni = pepperoni;
    }
}

Burger burger = new Burger(14, true, true, false, false);

As it can be seen, as the number of items to be added to the burger grows, we have to increase the number of arguments in the constructor too.

This is a very bad design practice because it becomes difficult to remember the order of arguments as well as what type of arguments are there in the arguments.

builder pattern

public class Burger {
    private int size;

    private boolean cheese = false;
    private boolean pepperoni = false;
    private boolean lettuce = false;
    private boolean tomato = false;

    public Burger(BurgerBuilder builder) {
        this.size = builder.size;
        this.cheese = builder.cheese;
        this.pepperoni = builder.pepperoni;
        this.lettuce = builder.lettuce;
        this.tomato = builder.tomato;
    }
}

public class BurgerBuilder {
    public int size;

    public boolean cheese = false;
    public boolean pepperoni = false;
    public boolean lettuce = false;
    public boolean tomato = false;

    public BurgerBuilder(int size) {
        this.size = size;
    }

    public BurgerBuilder addPepperoni() {
        this.pepperoni = true;
        return this;
    }

    public BurgerBuilder addLettuce() {
        this.lettuce = true;
        return this;
    }

    public BurgerBuilder addCheese() {
        this.cheese = true;
        return this;
    }

    public BurgerBuilder addTomato() {
        this.tomato = true;
        return this;
    }

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


Burger burger = (new BurgerBuilder(14))
                    .addPepperoni()
                    .addLettuce()
                    .addTomato()
                    .build();

In the BurgerBuilder class, all the add method returns the builder type so that we can form the chain for subsequent addition.

Finally, build method returns the Burger which is what we want.

Make sure you implement this design pattern according to the context otherwise it will be an overkill. :D

Discussion

pic
Editor guide
Collapse
mkuegi profile image
Markus Zancolò

I always wonder: why the need of an extra builder? Wouldn't it be sufficient to just add the "withTomato()" etc calls to the burger itself?

Collapse
qqlis profile image
Gregor K.

The builder pattern is very common when you use immutable value objects and you don't want any change to happen after object creation.
This example doesn't show it, but I would personally make all Burger attributes final, the BurgerBuilder an static inner class of Burger and the Burger(BurgerBuilder) constructor private.

Collapse
thomaswdmelville profile image
Thomas Melville

I've been learning about patterns for the last while, I have one thought on the static implementation. Making the builder class static could lead to concurrency issues.

Thread Thread
qqlis profile image
Gregor K.

No, just from the static keyword on a class you can't tell whether the class is thread safe or not.

The static keyword on a enclosed class means that the class is treated like a top-level class. It can be instantiated independently from the outer class and has no reference to an instance of the outer class.
A "real" non-static inner class can only be instantiated with an instance of the outer class and it will have access to the members of the outer class. This would not work for a Builder.

See also Java Language Specification §8.5.1:
docs.oracle.com/javase/specs/jls/s...

There could be concurrency issues if you would share the Builder instance with other threads. But this is independent from the Builder being static or not and it would be a quite unusual case to access the builder concurrently.

Thread Thread
thomaswdmelville profile image
Thomas Melville

Thanks Gregor,

In my case, the builder would be shared between threads so I could see the concurrency issue of using static. Automated user acceptance tests which execute in parallel in the same jvm.
I have the choice of making the builder thread safe or blocking users from instantiating the object without that builder.

Thread Thread
qqlis profile image
Gregor K.

Like I said making the builder a static inner class or a top-level class makes no difference regarding concurrency.

Each thread creates its own Builder object that is not shared between threads. Each thread fills it individually and calls build(). Afterwards you have a perfectly thread safe immutable object that you can make available to multiple thread.

The Builder object itself is not thread safe. But it doesn't need to be, since it should not be shared between threads. Sharing the Builder object between threads circumvents the whole point of this pattern and I would say its quite bad design.

So this is thread safe unless you use it in a unsafe way.

Thread Thread
thomaswdmelville profile image
Thomas Melville

I agree, the internals of the class (i.e. non final fields) would determine if it's thread safe or not.

I agree, in the case where the builder is static is not thread safe. Why shouldn't it be shared between threads though? My understanding of this pattern is that it's about object creation, in a way that makes the code more maintainable and reduces the chances of objects with incorrect state. I haven't seen any mention of concurrecny.

I've put 3 possible builder implementations in my pattern-play repo, as you may have guessed I use it for learning patterns
github.com/ethomev/pattern-play/tr...

I like the pattern where there is only one constructor for the object and it takes the builder. I think I will use this implementation the next time I need to use the builder pattern.

P.S. Sorry for the delay in replying, I'm now a father of 2 so I have less free time ;-)

Collapse
nishparadox profile image
Nishan Pantha Author

Yeah. That will be more concrete. The tutorial is just a rough pattern to get the insights.

Collapse
mkuegi profile image
Markus Zancolò

Good point. I had an other case in mind were the objects are created in different ways and may get additional fields set at a later stage.

Collapse
monknomo profile image
Gunnar Gissel

I'm a huge fan of returning this with java builder patterns. The fluent api you get out of it is fantastic. You can very nearly get something like a custom DSL out of just that technique alone

Collapse
nishparadox profile image
Nishan Pantha Author

True that. The chain rules are elegant and are more aesthetic in builder patterns.

Collapse
feacur profile image
Leonid Kapitonov

Thank you for the article.

I want to add couple-o-things. I'm sorry if I sound pushy, please feel free to parry my notes and point my mistakes.

Please, oh please, do use named arguments for vague stuff (concerning methods as well):

  • C#: Burger burger = new Burger(size: 14, cheese: true, tomato: true, lettuce: false, pepperoni: false);
  • Java: Burger burger = new Burger(/*size*/ 14, /*cheese*/ true, /*tomato*/ true, /*lettuce*/ false, /*pepperoni*/ false);
  • Kotlin: Burger burger = new Burger(size = 14, cheese = true, tomato = true, lettuce = false, pepperoni = false);

Remember about default values, which is a thing in C# and Kotlin. Sorry, Java.

Also you can place these setters inside original Burger class. Unfortunately, this way object initialization wouldn't be atomic anymore.

I just checked google, so I wouldn't suggest using double braces initialization for Java. C# initializers are OK, though, and atomic. I do not know of the same stuff for Kotlin, but its syntax for constructors is great in the first place.

Basically, the problem grows out of Java itself, because I see more elegant solutions in other languages like C# and Kotlin. At least more complex example required in order to favour builders and factories.

Collapse
miffpengi profile image
Miff

If you don't need your properties to be necessarily set at creation, you can also set properties when constructing the object in C# even if they're not in the constructor.

var burger = new Burger(14){ Cheese = true, Tomato = true, Lettuce = false, Pepperoni = false };
Collapse
jesuszilla_tm profile image
Jesuszilla イエスジラ

Yeah this is definitely pretty specific to Java and other languages that don't have that nice syntactic sugar C# does. Articles like these really make me realize how much C# spoils us!

Collapse
nishparadox profile image
Nishan Pantha Author

True that. It'd be much easier if there was a concept like named argument in languages like Java.

Collapse
chaniro profile image
Bastien BARET

A cool way of using builder design pattern when you want mandatory parameters:

public interface IRequestBuilderWithoutMethod {
    IRequestBuilderWithoutTimeout setMethod(HttpMethod method);
}


public interface IRequestBuilderWithoutTimeout {
    ICompletedRequest setTimeout(long timeout);
}

public interface ICompletedRequest {
    HttpResponse execute();
}
HttpRequestBuilder builder = HttpRequestBuilder.create('https://dev.to') //Return an IRequestBuilderWithoutMethod 
    .setMethod(HttpMethod.GET) //Return an IRequestBuilderWithoutTimeout 
    .setTimeout(2000L) //Return an ICompletedRequest 
    .execute();
Collapse
helpermethod profile image
Oliver Weiler

I prefer Java 8 style builders

Burger.of(14, ingredients -> ingredients
    .pepperoni()
    .lettuce()
    .tomato());

You can easily create an overload which accepts only the mandatory arguments

Burger.of(14);

The configuration is implemented as a Consumer of type T, where T is a some sort of configuration object.

See Molten JSON for a JSON Builder implemented that way.

Collapse
jtlunsford profile image
Joshua Lunsford

I wrote the burger builder in my own preferred pattern. dev.to/jtlunsford/build-a-differen... Didn't care much for the chaining though I do find chaining nice if I need to pass the object around my application before we call it finished

Collapse
nishparadox profile image
Nishan Pantha Author

Cool. Yeah, it's all about the use cases in the end.

Collapse
dallgoot profile image
dallgoot

that example is so OOP overkill that i don't know where to start

first:

new BurgerBuilder(14)).addPepperoni().addLettuce().addTomato().build()

instead of

new Burger(14, false, true, true, true)

1 constructor VS 1 constructor + 4 methods call, what's the gain ?

second:
you got 2 objects creations instead of 1
what's the gain ?

third: immutability

public class Burger {
    private int size;

    private boolean cheese = false;
    private boolean pepperoni = false;
    private boolean lettuce = false;
    private boolean tomato = false;

    public Burger(BurgerBuilder builder) {
        this.size = builder.size;
        this.cheese = builder.cheese;
        this.pepperoni = builder.pepperoni;
        this.lettuce = builder.lettuce;
        this.tomato = builder.tomato;
    }
}

is already immutable and the builder is the one thas is not with the added methods
what are the "addX" methods for ? if immutability is the goal ?

this example fails to display the purpose of the builder pattern IMHO

for another idea of implementation:

new Burger(size, ingredients)

with size and ingredients being higher representation (Dict/Set/HashMap)for example:
size can be a set of "big, small, medium"
ingredients can be "pepperonni: true" or "cheese: new Cheese("gouda")"
We have to kill this trend to make code for making code and remember that code is a higher level of expressing a solution/process.

Collapse
stealthmusic profile image
Jan Wedel

Just use Lombok.

@Builder
public class Burger {
    private int size;

    private boolean cheese = false;
    private boolean tomato = false;
    private boolean lettuce = false;
    private boolean pepperoni = false;
}
Collapse
schlump_fuffzn profile image
Stefan Hecker

Aren't you duplicating the set of Boolean parameters? Why not create an instance of Burger in the BurgerBuilder constructor and return it when calling build().
Oh, I see, that way Burger won't be immutable.

Collapse
nishparadox profile image
Nishan Pantha Author

Yeah. The whole point is to avoid the constructor of Burger being populated. This way, it will allow you to create chained calls to the builder and yes the side effect is the immutability of Burger subsequently.

Collapse
ghost profile image
Ghost

I just realised I've never really understood it before. I think the difference comes from the pragmatic use case, aka "the constructor has loads of arguments" + a nice a short none specific example. I'd like to read something this clear for every design pattern.

Collapse
nishparadox profile image
Nishan Pantha Author

Thanks. I might do similar post for other patterns I have​been using.

Collapse
wrongabouteverything profile image
wrong-about-everything

If a class has a constructor with a lot of arguments, the problem is in this class. Using a builder pattern just masks the problem, which is even worse. So I consider a builder an anti-pattern. I'd suggest to use a decorator instead:

interface Burger
{

}

class BreadWithCumin implements Burger
{
    private $size;

    public function __construct(Size $size)
    {
        $this->size = $size;
    }
}

class WithTomatoes implements Burger
{
    public function __construct(Burger $burger)
    {
    }
}

class WithCheese implements Burger
{
    public function __construct(Burger $burger)
    {
    }
}

If you object that Bread is a Burger, how about that:

class EmptyBurger implements Burger
{
    private $bread;

    public function __construct(Bread $bread)
    {
        $this->bread = $bread;
    }
}

class WithCumin implements Bread
{
    private $size;

    public function __construct(Size $size)
    {
        $this->size = $size;
    }
}

This code is highly composable, which probably means that it's good in terms of OOP.

Collapse
sunvim profile image
mobius

In this code, can you tell me what font you use? It's very nice

Collapse
pierrre profile image
Pierre Durand

right click > inspect > "font-family: monospace;"
It's the default monospace font for your current environment:

  • Consolas on my Windows PC
  • Noto Monospace on my Linux PC
Collapse
sunvim profile image
mobius

Thanks

Collapse
nishparadox profile image
Nishan Pantha Author

I am using the default one that dev.to is providing.

Collapse
lilbond profile image
lilbond

There is a lot more to builder than just number, order and type of constructor arguments. Nice try though!!!

Collapse
belgoros profile image
Serguei Cambour

There is an error in sizevariable declaration in BurgerBuilder class:

public size;

instead of:

public int size;

The same is for Burger class.

Thank you for sharing.

Collapse
nishparadox profile image
Nishan Pantha Author

thanks for pointing out the typo...

Collapse
mkdotam profile image
mikayel ghazaryan

You missed comma between 3 and 4 argument in bad example object initiation: new Burger(14, true, true false, false);

Sorry for throwing syntax errors to your face :D

Collapse
nishparadox profile image
Nishan Pantha Author

haha thanks.. No problem. Didn't see that typo.. :D

Collapse
al_tameh profile image
ضاد جيم ياء جيم

Plz cover the rest of the patterns with such magical style! Thanks for this piece of brilliance!

Collapse
fzngagan profile image
fzngagan

why don't we write those methods in the Burger class itself...?

Collapse
adrientorris profile image
Adrien Torris

There is a really great post about design patterns on GitHub, if you want to dig about this : github.com/kamranahmedse/design-pa...