DEV Community

The Builder Pattern in Java, and Dart Cascades

Jake Varness on December 06, 2017

Object construction is something that everyone will have to do in a language that has object-oriented paradigms. However, when your objects have a ...
Collapse
 
marcguilera profile image
Marc Guilera • Edited

Good example. I like the idea but you point out 2 advantages of the builder pattern: First, it is easier to instantiate (or build) objects and second, is object immutability. With the dart cascade option, you lost the object immutability you had in the Java version.

I guess if we want that we have two option: make a builder in Dart or have a mutable class (with setters) subclass the immutable class (with getters) and use the mutable class with the cascade idea but once constructed pass around the superclass. I personally prefer the builder option in this case.

Collapse
 
jvarness profile image
Jake Varness

Excellent points Marc. I think with Dart it would be pretty easy to make a builder that creates an immutable object, and cascades can be easily used with a builder!

Collapse
 
marcguilera profile image
Marc Guilera • Edited

Absolutely! I usually create a builder that returns itself on each method to follow the usual builder pattern and make it easier for devs coming from other languages but the consumer can always choose to use cascading instead.

I really value immutability in my code so it's obvious from the API that after instantiation an object is not meant to be changed. Take a DI container for example, after setting it up with an InjectorBuilder (with registerFactory, registorSingleton, etc) I get an Injector that I pass around and only allows gets.

To your point of not having to copy the object to instantiate it from the builder and continuing with the Injector example, I have an Injector interface (or abstract class in Dart) and an InjectorImpl (which implements Injector + the setter methods). Then from the builder, I create a private instance of InjectorImpl which I modify through the builder and then on build return it as an Injector (basically a getter). This is an option if instantiating that object is not that expansive. I do not expose InjectorImpl to the outside world.

Collapse
 
aikar profile image
Daniel Ennis

Even better solution: Allow cascade operators on final parameters during object instantiation as shown in the example and map that to the initialized and final value.

Collapse
 
fpuffer profile image
Frank Puffer

I probably missed something but how do you make your Pizza immutable in Java? To make all the private attributes of Pizza accessible from PizzaBuilder, they need public setters which in my understanding contradicts immutability.

Well, there is a way to resolve this, which you did not mention in your article: You can make PizzaBuilder an inner class of Pizza. Then is can access the private attributes. It works but I am afraid it often makes your code harder to comprehend.

Another thing that should be mentioned: What you describe is the Builder pattern by Joshua Bloch, not the one by the Gang of Four. There are some similarities between the two as both of them separate object construction from object usage. But basically they are different things.

Collapse
 
jvarness profile image
Jake Varness

That's correct, making your builder an inner class is a good way of accomplishing this!

Collapse
 
inakiarroyo profile image
Iñaki Arroyo

At the moment there is not possible to add inner classes in Dart :(

Collapse
 
florianschaetz profile image
(((Florian Schätz)))

Just a little detail:

Personally, I prefer the following:

Instead of letting the builder call all the getters/setters (or even set the fields) directly, you can create a Pizza constructor that takes a builder instance and gets the values from there (personally, I tend to make the builder a static inner class of the class it instantiates):

private Pizza(PizzaBuilder builder) {...}

This has some slight advantages, most of all that you can keep your Pizza object immutable, if you want to (why would you change the crust after baking it?), because all your members can be final and all your collections can be immutableList/Set/etc.

Collapse
 
jvarness profile image
Jake Varness

Exactly, that's an excellent point to make! That's what I was getting at with the ability to make something immutable.

Collapse
 
inakiarroyo profile image
Iñaki Arroyo • Edited

A possible improvement would be adding a named constructor 'Pizza.builder()' which not allow consumers to directly create an instance through the default constructor, giving to the consumer the context that the class is following the builder pattern.

Pizza pizza = Pizza.builder()
..toppings = ['pepperoni', 'mushrooms']
..sauce = 'spaghetti'
..hasExtraCheese = true;

I've written a post following your Pizza example about my own implementation of the Builder pattern :)

Collapse
 
abelagoi profile image
Agoi Abel Adeyemi

Men, you are doing great.
Posting almost every day. thats cool

Collapse
 
jvarness profile image
Jake Varness

Haha thanks Agoi!

It's probably not going to always be like this, but I appreciate the support :)

Collapse
 
jvarness profile image
Jake Varness

Crap... Yes, thank you for pointing that out.