Create Your Own Builder Pattern in Kotlin from Scratch
System Design
Create Your Own Builder Pattern in Kotlin from Scratch
Akshay Nandwana
January 4, 2025
5 min read
53 views

Design patterns are an essential part of software development. Among these, the Builder Pattern stands out for its ability to create complex objects step-by-step. While Kotlin provides powerful features like apply, with, and named arguments that often eliminate the need for classic patterns, implementing the Builder Pattern from scratch is still an excellent exercise to understand object creation and design principles.

In this blog, we'll build our own version of the Builder Pattern in Kotlin. Let's dive in!

What is the Builder Pattern?

The Builder Pattern is a creational design pattern used to construct complex objects step-by-step. Instead of creating the object in a single constructor call, the Builder Pattern allows us to build it incrementally, setting various properties along the way.

Key Characteristics:

  1. Step-by-step construction: Build the object one piece at a time.

  2. Immutability: The final object is often immutable once built.

  3. Fluent Interface: Methods return the builder itself, allowing for a chainable API.

When to Use the Builder Pattern?

The Builder Pattern is useful when:

  • You need to construct an object with many optional or configurable parameters.

  • The object initialization logic is complex.

  • You want to make your code more readable and flexible.

Step-by-Step Implementation

Let's take the example of building a RajasthanTrip class with properties like destination, duration, activities, and luxuryAccommodation.

1. Define the Target Class

First, define the RajasthanTrip class with properties:

kotlin
class RajasthanTrip private constructor(
    val destination: String,
    val duration: Int, // in days
    val activities: List<String>,
    val luxuryAccommodation: Boolean
) {  

override fun toString(): String {
        return "RajasthanTrip(destination='$destination', duration=$duration days, activities=$activities, luxuryAccommodation=$luxuryAccommodation)"
    }
}

2. Create a Builder Class

This is where we define mutable properties and methods to configure the RajasthanTrip object step-by-step.

kotlin
companion object {
        // A function to easily create a RajasthanTrip using a DSL
        fun create(
            destination: String = "Jaipur",
            duration: Int = 3,
            luxuryAccommodation: Boolean = false,
            buildActivities: MutableList<String>.() -> Unit = {}
        ): RajasthanTrip {
            val activities = mutableListOf<String>().apply(buildActivities)
            return RajasthanTrip(destination, duration, activities, luxuryAccommodation)
        }
    }

3. Use the Builder Class

Now, let's use the builder to construct a RajasthanTrip object:

kotlin
fun main() {
    // Using the DSL-style builder for easy construction
    val trip = RajasthanTrip.create(
        destination = "Udaipur",
        duration = 5,
        luxuryAccommodation = true
    ) {
        add("Visit City Palace")
        add("Lake Pichola Boat Ride")
    }

    println(trip)
}

Output:

javascript
RajasthanTrip(destination='Udaipur', duration=5 days, activities=[Visit City Palace, Lake Pichola Boat Ride], luxuryAccommodation=true)

Kotlin's apply and Builder Pattern

Kotlin's apply and named arguments often eliminate the need for a traditional Builder Pattern. For example, the same RajasthanTrip object could be constructed using:

kotlin
val trip = RajasthanTrip(
    destination = "Udaipur",
    duration = 5,
    activities = listOf("Visit City Palace", "Lake Pichola Boat Ride"),
    luxuryAccommodation = true
)

However, the Builder Pattern becomes invaluable when dealing with more complex object creation logic or when maintaining backward compatibility in large codebases.

Why Write Your Own Builder?

  1. Customization: You can add specific validation rules during object creation.

  2. Readability: A fluent, chainable API improves code clarity.

  3. Code Maintenance: Decouples the construction logic from the main class.

Advanced: Add Validation

You can enhance the builder with validation logic. For instance, ensuring a RajasthanTrip has at least one activity:

kotlin
fun build(): RajasthanTrip {
    if (activities.isEmpty()) {
        throw IllegalArgumentException("RajasthanTrip must have at least one activity")
    }
    return RajasthanTrip(destination, duration, activities, luxuryAccommodation)
}

Conclusion

The Builder Pattern is a classic design pattern that provides a structured and readable way to construct objects. While Kotlin's features often simplify the need for traditional patterns, implementing the Builder Pattern from scratch gives you insights into object creation and design principles.

Try creating your own builder for another class and see how it transforms your code! Let me know your thoughts or share your implementation in the comments below.

Akshay Nandwana
Founder AndroidEngineers

You can connect with me on:


Book 1:1 Session here
Click Here

Join our upcoming classes
https://www.androidengineers.in/courses

Share This Article
Stay Updated

Get the latest Android development articles delivered to your inbox.