Builder Pattern

In this blog we are going to cover several aspects about the builder pattern in Kotlin. We have a look on how to create a builder pattern and if you should use it in Kotlin.

Configuration of objects

Many programmers use a pattern to chain member function calls, in order to avoid retyping the name of an object. This pattern is commonly used in conjunction with the builder pattern itself. One has to be careful because some people are mixing the terms and saying that the chaining IS the builder pattern (which it is not). As a matter of fact some developers will complain that it is not the “Kotlin way” (e.g. in some forums in StackOverflow). We think however that this way of configuring objects is known to many programmers and very explicit.

An example is the following code.

    // Without Chaining

    val person = Person()
    person.setName("Tom")
    person.setAddress("Street")
    person.setAge(18)
    // With Chaining

    val person = Person()
    person
        .setName("Tom")
        .setAddress("Street")
        .setAge(18)

The problem with the first approach is that the objects name has to be rewritten in every line. This leads to potential bugs because of typical copy-paste errors. The following code compiles correctly but is not what you want. This kind of errors happen frequently when doing a copy and paste.

// Copy paste error
val person = Person()
person.name = “Tom”
person.address = “Street”
person.age = 18

val person2 = Person()
person.name = “Tom”
person.address = “Street”
person.age = 18

For that reason the second approach is way better. You need less often the name of object, which is modified. This leads to less bugs. To achieve such behavior, you need to return the object itself in the function call. Kotlin provides natively an extension function apply which does the job for you.

Scoped Function – Apply

The extension function apply takes a lambda function and gives access to all public variables of the object which is configured. The advantage of using apply is that it is build in, so it does not need any boiler-plate code. Especially for data classes this is the best way. In the next code example we show first how to use the chaining in the traditional way. That’s the version which most programmers are familiar with. The second version is using Kotlin’s build-in functionality. As the second version needs less code it is the preferred way.

// General way
class Person() {
    var name = ""
    var address = ""
    var age = 0
    
    fun setName(name : String) : Person {
        this.name = name
        return this
    }
    
    fun setAddress(address : String) : Person {
        this.address = address
        return this
    }
    
    fun setAge(age : Int) : Person {
        this.age = age
        return this
    }       
}

// Using apply
class Person() {
    var name = ""
    var address = ""
    var age = 0
    
    fun setName(name : String) = apply {
        this.name = name
    }
    
    fun setAddress(address : String) = apply {
        this.address = address
    }
    
    fun setAge(age : Int) = apply {
        this.age = age
    }      
}

Note that apply can also be used at the client side (function which uses the object) as an extension function. This is in particular useful for data classes. In this way, however, you can not access private members / function (as it is expected).

fun main() {
    val person = Person()
    person
        .setName("Tom")
        .setAddress("Street")
        .setAge(18)
    

    val person2 = Person()
    person2.apply { 
        name = "Tom"
        address = "Street"
        age = 18
        // only access to public properties
    }
}

Lombok library

Kotlin provides a experimental Lombok compiler plugin. To use it you have to install the plugin as discussed in KotlinLang. As it is still experimental we don’t recommend using it. The Kotlin Lombok compiler plugin allows the generation and use of Java’s Lombok declarations by Kotlin code in the same mixed Java/Kotlin module. If ready it would allow annotations such as @builder, which would create automatically all required code.

Applying Business Rules

Another aspect of using this design pattern, is that it gives a perfect place to provide a dedicated software layer. This layer is completely responsible for applying business rules about the creation of domain objects. In the following example the domain object “Person” can be build by using the PersonBuilder. The PersonBuilder holds all relevant logic to check if a Person can be build. Once again this is not the actual “builder pattern” as described in book of GoF.

class Person() {
    var name = ""
    var address = ""
    var age = 0
}

class PersonBuilder() {

    private var name = ""
    private var address = ""
    private var age = 0

    fun setName(name : String) = apply {
        this.name = name
    }

    fun setAddress(address : String) = apply {
        this.address = address
    }

    fun setAge(age : Int) = apply {
        this.age = age
    }

    fun canBuild() : Boolean {
        // do business rules, checks
        return true
    }

    fun build() : Person {
        val person = Person()
        if(canBuild()) {
            person.address = address
            person.name = name
            person.age = age
        }
        return person
    }
}

Separate configuration and instantiation

Another reason to use this pattern is to separate the construction and configuration of an object. This is not only because it might be easier to separate these two concerns. But also it might be that not all information is available at the time. One can imagine that in a user interface we could configure a popup. When clicking on a launch button the popup is created. The creation and configuration are entirely separated by time.

DSL language

Another aspect of the builder design pattern is to create DSL languages. DSL stands for domain-specific languages. Kotlin is able to use well-named functions as builders in combination with function literals as reeivers, o create type-safe, statically-typed builders. This allows to create type-safe domain-specific languages (DSLs) suitable for building complex hierarchical data structures in a semi-declarative way. Imagine for example the following code

// Composed objects
class Address {
    var city = ""
    var street = ""
}

class Person {
    var name = ""
    var age = 0
    var address = Address()
}
    // Declarative writing
val person = person {
        name = "John"
        age = 18

        address {
            city = "New York"
            street = "Main Street"
        }
    }

To achieve this you have to play around with functions, function names and lambda functions. In this case are using the following:

class Address {
    var city = ""
    var street = ""
}

class Person {
    var name = ""
    var age = 0
    var address = Address()

    fun address(init: Address.() -> Unit) {
        val address = Address()
        address.init()
        this.address = address
    }
}


fun person(init: Person.() -> Unit): Person {
    val person = Person()
    person.init()
    return person
}

What does this code do? First of all we have declared a functions called “person“. This functions accepts a lambda function (init) with the Context of a person. This allows to access public properties in the lambda function. Secondly we have created a new function “address” in Person class. This function is similar to the one of Person.

NEW: Kotlin TypeSafe DSL Builder

We have created a completely new article about the pros, cons, and best practices with the Kotlin Domain Specific Language support.

Builder pattern in Kotlin

From now one we are going to show how to implement the actual builder pattern in Kotlin. We refer to the actual way as it is described in the book “Design Patterns”. However we are not going in such a detail as in the book. We will focus more on the implementation side of Kotlin.

Separate the construction of a complex object from its representation so that the same construction process can create different representations.

Builder – Design Pattern / GoF
builder_pattern_in_kotlin

The general structure is the following. A base builder class (AbstractBuilder) is created which defines certain functions (actionA(), etc). In the base implementation these functions are usually empty. They will be overwritten (if necessary) in the concrete implementation of the builder classes (ConcreteBuilderA etc). These concrete implementations define as well a build function which returns a certain product. Note that in the products might be very different. Therefore they don’t necessary need to have the same interface.

Alternative : Abstract Factory

A common alternative to the builder pattern is the use of the abstract factory. This design pattern deals as well about the separation of the construction process of objects. The difference is that the abstract factory returns abstract objects, while usually the builder pattern returns concrete objects. In the abstract factory the clients do not need to know which kind of object is instantiated, while in the builder pattern they know.

Private Constructor

To provide even more safety, it is possible to declare the constructors of the concrete objects private. The only public constructor would accept a builder which is building the object in the init block. More info can be found in What is the class constructor?.

Example

Consider the following example. In a user interface, information for a popup can be defined. This includes Title, Text, ActionButton and CancelButton. The information is stored as json, xml and as Widget. The creation of the three different forms is done by the builders.