Avoiding the Builder Design Pattern in Kotlin

The Builder Design Pattern provides us with a series of methods that can act as an aid in order to let the consumer of your class better understand what is happening under the hood.

But it is considered an Anti-Pattern by some developers.

Why use Builder Design Pattern?

It's an alternative to using multiple constructors by overloading them with more and more parameters.

What are the pros?

There are tools that allow you to get hints of what the name of the parameter is, which may help if their names are informative, but it is not always the case. Like, for example: reading code on GitHub or another IDE/editor.

You don't need to have all the data required to pass it to your object right when you initialize it.

What are the cons?

You can end up having methods that require others to be ran in a certain order; otherwise, the consumer will run into issues if you implement it wrong.

The chain of methods can get really long depending on the implementation.

The consumer may forget to finish up the statement with the build() method and not get the results they expected.

Uses more memory resources.

How can we avoid it?

Default Parameters

Kotlin supports default parameters (Which is also available in other languages like C# and JavaScript).

funpow(base:Int,power:Int=2):Int{// ...}

This can work as an alternative to method overloading, since the consumer of this method can use it like this:

pow(2)// outputs 4pow(2,3)// outputs 8

Which can make our life easier by only having to maintain a single method.

Named Arguments

This allows our consumers to not only type in exactly what argument they want to assign to an exact parameter, but we can also reorder them in whatever way we want. This can be handy when dealing with "legacy code" that can be hard to understand because of how many parameters requires.

pow(base=4,power=3)// outputs 64pow(power=3,base=4)// also outputs 64

Can we combine them?

Of course, my dear Watson!

pow(base=3)// outputs 9

In the example above we are passing the base argument as a named argument and the power is using the default parameter value in our method signature.

Now that we know how to do this, we can use them to avoid the Builder Design Pattern in Kotlin.

First, let's check out the code we would have to write in Java by creating a simplified version of a Hamburger class:

// Hamburger.javapublicclassHamburgerJava{privatefinalbooleanhasKetchup;privatefinalbooleanhasTomatoes;privatefinalintmeats;privateHamburgerJava(Builderbuilder){hasKetchup=builder.hasKetchup;hasTomatoes=builder.hasTomatoes;meats=builder.meats;}publicstaticclassBuilder{privatebooleanhasKetchup;privatebooleanhasTomatoes;privateintmeats;publicBuilder(intmeats){if(meats>3)thrownewIllegalArgumentException("Cannot order hamburger with more than 3 meats");if(meats<1)thrownewIllegalArgumentException("A hamburger must have at least 1 meat.");this.meats=meats;}publicBuilderaddKetchup(booleanhasKetchup){this.hasKetchup=hasKetchup;returnthis;}publicBuilderaddTomatoes(booleanhasTomatoes){this.hasTomatoes=hasTomatoes;returnthis;}publicHamburgerJavabuild(){returnnewHamburgerJava(this);}}}

I like named parameters, they are convenient and quick to write. IMHO, a more readable approach are so called "withers", like ones from Immutables library. You can type "with" and have autocomplete suggestion for free. :)

Also, there are some problems if you care about binary compatibility when changing case classes in Scala, see here. IDK about Kotlin though.