Por que o Builder em Kotlin não é apenas um "copia e cola" do Java?
Se você já programou em Java, certamente já se deparou com o clássico padrão Builder. Dezenas de linhas de código, classes estáticas internas, métodos set e o final build(). Tudo isso funciona, mas parece verboso.
Agora imagine que você migrou para Kotlin. Uma linguagem criada para ser concisa e expressiva. E então você se depara com a tarefa: precisa criar um objeto com muitos parâmetros, alguns dos quais opcionais. Sua mão já quer escrever o Builder clássico do Java...
Pare! Em Kotlin, esse padrão pode ser implementado de forma 10 vezes mais simples, elegante e segura. Neste artigo, vamos explorar 4 maneiras de implementar o padrão Builder em Kotlin — desde a mais simples até a abordagem avançada com DSL.
Este material é baseado em uma pergunta real do Stack Overflow, que acumulou milhares de visualizações. Nós a adaptamos para conteúdo educacional, para que você possa aplicar o conhecimento imediatamente na prática.
Problema: quando o Builder é necessário?
Imagine que você tem uma classe Pizza:
class Pizza( val size: String, val cheese: Boolean = false, val pepperoni: Boolean = false, val mushrooms: Boolean = false, val olives: Boolean = false, val extraCheese: Boolean = false)Até parece tudo bem. Mas quando você cria uma pizza com 5 parâmetros opcionais, o código se torna ilegível:
val pizza = Pizza("Large", true, false, true, false, true)Ficar procurando com os olhos qual parâmetro significa o quê é angustiante. É aí que o padrão Builder vem ao resgate.
Método 1. Builder clássico estilo Java (para compatibilidade)
Se você precisa manter compatibilidade com código Java ou deseja uma API explicitamente clara, pode usar a abordagem clássica:
class Pizza private constructor( val size: String, val cheese: Boolean, val pepperoni: Boolean, val mushrooms: Boolean, val olives: Boolean, val extraCheese: Boolean) { class Builder(private val size: String) { private var cheese: Boolean = false private var pepperoni: Boolean = false private var mushrooms: Boolean = false private var olives: Boolean = false private var extraCheese: Boolean = false
fun cheese(value: Boolean) = apply { cheese = value } fun pepperoni(value: Boolean) = apply { pepperoni = value } fun mushrooms(value: Boolean) = apply { mushrooms = value } fun olives(value: Boolean) = apply { olives = value } fun extraCheese(value: Boolean) = apply { extraCheese = value }
fun build() = Pizza(size, cheese, pepperoni, mushrooms, olives, extraCheese) }}
// Uso:val pizza = Pizza.Builder("Large") .cheese(true) .pepperoni(true) .mushrooms(true) .build()Prós: código óbvio, compreensível para desenvolvedores Java.
Contras: muito código boilerplate, não aproveita os recursos do Kotlin.
Método 2. Estilo Kotlin com argumentos nomeados e valores padrão
A maneira mais simples e frequentemente a melhor é não usar Builder, mas sim confiar nos recursos da própria linguagem:
data class Pizza( val size: String, val cheese: Boolean = false, val pepperoni: Boolean = false, val mushrooms: Boolean = false, val olives: Boolean = false, val extraCheese: Boolean = false)
// Uso:val pizza = Pizza( size = "Large", cheese = true, pepperoni = true, mushrooms = true)Isso é um código limpo, legível e type-safe. Os argumentos nomeados do Kotlin resolvem o problema de "números mágicos" e da ordem dos parâmetros.