Builder Design Pattern in Kotlin

1. Definition

The Builder Design Pattern separates the construction of a complex object from its representation, allowing the same construction process to create different representations.

2. Problem Statement

Consider a scenario where you need to create a complex object with multiple parts. Direct constructors might be inadequate due to the excessive number of parameters, or the order of construction might vary. Also, object instantiation might require more steps than simple field assignments.

3. Solution

The Builder Pattern suggests moving the construction logic out of the main class and placing it inside separate builder objects. This allows for step-by-step construction, producing different types or configurations of objects by using different specific builders.

4. Real-World Use Cases

1. Constructing meal combos in fast-food chains.

2. Building custom computers with various configurations.

3. Constructing detailed 3D models in graphics software.

5. Implementation Steps

1. Define a builder interface that outlines the construction steps.

2. Implement the builder interface in a concrete builder class.

3. Introduce a director class that will use the builder interface to create a product.

4. Clients can then use the director with a specific builder to produce a particular product configuration.

6. Implementation in Kotlin

// Step 1: Product Class
data class Computer(val CPU: String, val RAM: String, val storage: String)

// Step 2: Builder Interface
interface ComputerBuilder {
    fun setCPU(cpu: String): ComputerBuilder
    fun setRAM(ram: String): ComputerBuilder
    fun setStorage(storage: String): ComputerBuilder
    fun build(): Computer
}

// Step 3: Concrete Builder
class GamingComputerBuilder : ComputerBuilder {
    private var CPU = "Default CPU"
    private var RAM = "Default RAM"
    private var storage = "Default Storage"

    override fun setCPU(cpu: String): ComputerBuilder {
        this.CPU = cpu
        return this
    }

    override fun setRAM(ram: String): ComputerBuilder {
        this.RAM = ram
        return this
    }

    override fun setStorage(storage: String): ComputerBuilder {
        this.storage = storage
        return this
    }

    override fun build() = Computer(CPU, RAM, storage)
}

// Step 4: Director Function
fun createGamingComputer(builder: ComputerBuilder): Computer {
    return builder.setCPU("High-end CPU")
                 .setRAM("16GB RAM")
                 .setStorage("1TB SSD")
                 .build()
}

fun main() {
    val gamingComputerBuilder = GamingComputerBuilder()
    val gamingComputer = createGamingComputer(gamingComputerBuilder)
    println(gamingComputer)
}

Output:

Computer(CPU=High-end CPU, RAM=16GB RAM, storage=1TB SSD)

Explanation:

1. We defined a simple Computer class that will represent our final product.

2. The ComputerBuilder interface outlines methods for setting parts of the product and a build method to finalize the creation.

3. The GamingComputerBuilder is a concrete builder implementing the defined interface. It provides a chainable API for configuration.

4. A director function createGamingComputer uses the builder to create a computer with gaming specifications.

5. In the main function, we use the director with a specific builder to create and print a gaming computer.

7. When to use?

The Builder Pattern is ideal when:

1. The algorithm to create a complex object should be independent of its parts and their assembly.

2. The construction process should allow different representations of the constructed object.

3. Object creation involves multiple steps or requires input in various orders or forms.

Using the Builder pattern, we can ensure that a product is produced according to specific requirements and configurations.


Comments