Einführung in Entwurfsmuster in Kotlin
Entwurfsmuster (Design Patterns) sind bewährte Lösungen für typische Entwicklungsaufgaben. In der Sprache Kotlin werden viele klassische Muster dank integrierter Funktionen wie Null-Sicherheit, Erweiterungsfunktionen, Coroutinen und einem leistungsstarken Typsystem prägnanter und sicherer implementiert.
In diesem Artikel behandeln wir drei Gruppen von Mustern: Erzeugungsmuster, Strukturmuster und Verhaltensmuster – mit realen Beispielen in Kotlin. Sie erfahren, wie Sie sie in der täglichen Entwicklung einsetzen, um eine flexible und wartbare Architektur zu erstellen.
Erzeugungsmuster (Creational)
1. Singleton (Einzelstück)
In Kotlin wird ein Singleton mit dem Schlüsselwort object implementiert. Dies ist ein threadsicherer, fauler Singleton ohne überflüssigen Code.
object DatabaseConnection { private val url = "jdbc:postgresql://localhost:5432/mydb" private val user = "admin" private val password = "secret"
fun connect(): String { return "Connected to $url as $user" }}
fun main() { println(DatabaseConnection.connect()) // Prüfen, ob es dasselbe Objekt ist val ref1 = DatabaseConnection val ref2 = DatabaseConnection println(ref1 === ref2) // true}Vorteil: Man muss kein synchronized oder volatile schreiben – Kotlin garantiert eine einzige Instanz.
2. Factory Method (Fabrikmethode)
Ermöglicht es, die Objekterstellung an Unterklassen zu delegieren. In Kotlin ist die Verwendung von sealed class und companion object praktisch.
sealed class Notification { data class Email(val to: String, val subject: String) : Notification() data class SMS(val phone: String, val message: String) : Notification() data class Push(val deviceId: String, val title: String) : Notification()
companion object Factory { fun createEmail(to: String, subject: String): Notification = Email(to, subject) fun createSMS(phone: String, message: String): Notification = SMS(phone, message) fun createPush(deviceId: String, title: String): Notification = Push(deviceId, title) }}
fun send(notification: Notification) { when (notification) { is Notification.Email -> println("Email to ${notification.to}") is Notification.SMS -> println("SMS to ${notification.phone}") is Notification.Push -> println("Push to ${notification.deviceId}") }}
fun main() { val email = Notification.createEmail("user@example.com", "Hello") send(email)}3. Builder (Erbauer)
In Kotlin kann der Builder durch benannte Argumente und Standardwerte ersetzt werden. Für komplexe Objekte mit Validierung bleibt der klassische Builder jedoch relevant.
class Pizza private constructor( val size: String, val cheese: Boolean, val pepperoni: Boolean, val mushrooms: Boolean) { class Builder { private var size: String = "Medium" private var cheese: Boolean = false private var pepperoni: Boolean = false private var mushrooms: Boolean = false
fun size(size: String) = apply { this.size = size } fun cheese() = apply { this.cheese = true } fun pepperoni() = apply { this.pepperoni = true } fun mushrooms() = apply { this.mushrooms = true }
fun build(): Pizza { require(size in listOf("Small", "Medium", "Large")) { "Invalid size" } return Pizza(size, cheese, pepperoni, mushrooms) } }}
fun main() { val pizza = Pizza.Builder() .size("Large") .cheese() .pepperoni() .build() println("Pizza: ${pizza.size}, cheese=${pizza.cheese}, pepperoni=${pizza.pepperoni}")}Strukturmuster (Structural)
4. Adapter (Anpasser)
Der Adapter ermöglicht es Objekten mit un