Warum Builder in Kotlin nicht einfach "Copy-Paste" aus Java ist?
Wenn Sie jemals in Java programmiert haben, sind Sie mit ziemlicher Sicherheit auf das klassische Builder Pattern gestoßen. Dutzende von Codezeilen, innere statische Klassen, set-Methoden und der finale build()-Aufruf. Das funktioniert alles, wirkt aber umständlich.
Stellen Sie sich nun vor, Sie sind zu Kotlin gewechselt. Eine Sprache, die entwickelt wurde, um prägnant und ausdrucksstark zu sein. Und dann stehen Sie vor der Aufgabe: Ein Objekt mit vielen Parametern zu erstellen, von denen einige optional sind. Ihre Hand zögert, den klassischen Builder aus Java zu schreiben...
Stopp! In Kotlin kann dieses Pattern zehnmal einfacher, eleganter und sicherer implementiert werden. In diesem Artikel werden wir 4 Möglichkeiten zur Implementierung des Builder Patterns in Kotlin durchgehen – vom einfachsten bis zum fortgeschrittenen DSL-Ansatz.
Dieses Material basiert auf einer echten Frage von Stack Overflow, die tausende Aufrufe erhalten hat. Wir haben es für Bildungsinhalte angepasst, damit Sie das Wissen sofort in der Praxis anwenden können.
Problem: Wann wird ein Builder benötigt?
Stellen Sie sich vor, Sie haben eine Klasse 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)Das sieht zunächst gut aus. Aber wenn Sie eine Pizza mit 5 optionalen Parametern erstellen, wird der Code unlesbar:
val pizza = Pizza("Large", true, false, true, false, true)Mit den Augen zu suchen, welcher Parameter was bedeutet, ist mühsam. Hier kommt das Builder Pattern zur Hilfe.
Methode 1. Klassischer Java-Style Builder (für Kompatibilität)
Wenn Sie Java-Code unterstützen müssen oder eine möglichst explizite API wünschen, können Sie den klassischen Ansatz verwenden:
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) }}
// Verwendung:val pizza = Pizza.Builder("Large") .cheese(true) .pepperoni(true) .mushrooms(true) .build()Vorteile: Offensichtlicher Code, verständlich für Java-Entwickler.
Nachteile: Viel Boilerplate-Code, nutzt die Möglichkeiten von Kotlin nicht aus.
Methode 2. Kotlin-Stil mit benannten Argumenten und Standardwerten
Der einfachste und oft beste Weg ist, den Builder gar nicht zu verwenden, sondern sich auf die Sprachfunktionen zu verlassen:
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)
// Verwendung:val pizza = Pizza( size = "Large", cheese = true, pepperoni = true, mushrooms = true)Dies ist sauberer, lesbarer und typsicherer Code. Kotlins benannte Argumente lösen das Problem der "magischen Zahlen" und der Parameterreihenfolge.