DEV Community

Builder Design Pattern

Nishan Pantha on May 28, 2017

Main Points use builder design pattern when the constructor is very much populated by arguments. it becomes tedious for creating new ...
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 • Edited

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

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
 
feacur profile image
Leonid Kapitonov • Edited

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 };
Enter fullscreen mode Exit fullscreen mode
Collapse
 
nishparadox profile image
Nishan Pantha

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

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

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

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

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
 
stealthmusic profile image
Jan Wedel • Edited

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
 
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
 
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

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

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 • Edited

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
 
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

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
 
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 • Edited

There is an error in sizevariable declaration in BurgerBuilder class:

public size;
Enter fullscreen mode Exit fullscreen mode

instead of:

public int size;
Enter fullscreen mode Exit fullscreen mode

The same is for Burger class.

Thank you for sharing.

Collapse
 
nishparadox profile image
Nishan Pantha

thanks for pointing out the typo...

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

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

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...

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

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...?