Entwurfsmuster in Kotlin mit Beispielen
Entwurfsmuster sind bewährte Lösungen für typische Probleme bei der Softwareentwicklung. Sie helfen dabei, eine flexible, skalierbare und wartbare Architektur zu erstellen. In der Sprache Kotlin werden viele klassische Muster dank ihrer prägnanten Syntax und funktionalen Möglichkeiten besonders elegant umgesetzt. In diesem Artikel werden wir drei Schlüsselmuster behandeln: Singleton, Factory Method und Observer, und ihre Implementierung in Kotlin mit praktischen Beispielen zeigen.
1. Singleton (Einzelstück)
Das Singleton-Muster garantiert, dass es von einer Klasse nur eine Instanz gibt, und stellt einen globalen Zugriffspunkt darauf bereit. In Kotlin gibt es dafür eine eingebaute Konstruktion — object. Sie erstellt einen threadsicheren Singleton ohne überflüssigen Code.
Implementierung in Kotlin
Der einfachste Weg, einen Singleton zu deklarieren, ist die Verwendung des Schlüsselworts object:
object DatabaseConfig { private val url = "jdbc:postgresql://localhost:5432/mydb" private val username = "admin" private val password = "secret"
fun connect(): String { return "Verbindung zu $url als $username" }}
fun main() { println(DatabaseConfig.connect()) // Ausgabe: Verbindung zu jdbc:postgresql://localhost:5432/mydb als admin}Wenn ein klassischer Singleton mit privatem Konstruktor benötigt wird (z. B. für die Vererbung von einer anderen Klasse), verwenden Sie companion object:
class Logger private constructor() { fun log(message: String) { println("[LOG]: $message") }
companion object { private val instance: Logger by lazy { Logger() }
fun getInstance(): Logger { return instance } }}
fun main() { val logger = Logger.getInstance() logger.log("Anwendung gestartet")}Der Vorteil des Ansatzes mit by lazy ist die verzögerte Initialisierung, die nur beim ersten Zugriff ausgeführt wird.
2. Factory Method (Fabrikmethode)
Das Factory Method-Muster definiert eine Schnittstelle zur Erstellung eines Objekts, ermöglicht es aber Unterklassen, den Typ des erstellten Objekts zu ändern. In Kotlin wird dies oft durch sealed class oder Interfaces implementiert.
Implementierung in Kotlin
Angenommen, wir entwickeln ein Benachrichtigungssystem. Wir haben verschiedene Benachrichtigungstypen: Email, SMS und Push. Erstellen wir eine Fabrik zu ihrer Erzeugung.
// Schnittstelle für alle Benachrichtigungeninterface Notification { fun send(message: String)}
// Konkrete Implementierungenclass EmailNotification : Notification { override fun send(message: String) { println("Sende Email: $message") }}
class SMSNotification : Notification { override fun send(message: String) { println("Sende SMS: $message") }}
class PushNotification : Notification { override fun send(message: String) { println("Sende Push-Benachrichtigung: $message") }}
// Fabrikobject NotificationFactory { fun createNotification(type: String): Notification { return when (type.lowercase()) { "email" -> EmailNotification() "sms" -> SMSNotification() "push" -> PushNotification() else -> throw IllegalArgumentException("Unbekannter Benachrichtigungstyp: $type") } }}
fun main() { val notification = NotificationFactory.createNotification("sms") notification.send("Ihr Bestätigungscode: 1234") // Ausgabe: Sende SMS: Ihr Bestätigungscode: 1234}Wenn die Anzahl der Typen festgelegt ist, verwenden Sie besser sealed class für mehr Sicherheit:
sealed class NotificationType { object Email : NotificationType() object SMS : NotificationType() object Push : NotificationType()}
fun createNotification(type: NotificationType): Notificat