Entwurfsmuster in Kotlin mit Beispielen: Fortgeschrittenes Niveau
\n\nEntwurfsmuster sind bewährte Lösungen für typische Aufgaben in der Softwareentwicklung. Wenn Sie bereits die grundlegenden Muster (Singleton, Factory, Observer) beherrschen, ist es an der Zeit, die nächste Stufe zu erreichen. In diesem Artikel betrachten wir vier fortgeschrittene Muster – Chain of Responsibility, Interpreter, Mediator und Memento – und zeigen ihre Implementierung in Kotlin unter Berücksichtigung der Sprachidiome (sealed class, extension functions, Lambdas).
\n\n1. Chain of Responsibility
\n\nZweck: Ermöglicht die sequenzielle Weiterleitung einer Anfrage entlang einer Kette von Bearbeitern, bis einer von ihnen die Anfrage bearbeitet.
\n\nProblem: Ein Logging-System, bei dem Nachrichten verschiedener Stufen (DEBUG, INFO, ERROR) von verschiedenen Loggern verarbeitet werden sollen.
\n\nImplementierung in Kotlin:
\n\n// Abstrakter Bearbeiter\nabstract class Logger(private val level: Int) {\n private var nextLogger: Logger? = null\n\n fun setNext(logger: Logger): Logger {\n this.nextLogger = logger\n return logger\n }\n\n fun log(level: Int, message: String) {\n if (this.level <= level) {\n write(message)\n }\n nextLogger?.log(level, message)\n }\n\n protected abstract fun write(message: String)\n}\n\n// Konkrete Bearbeiter\nclass ConsoleLogger(level: Int) : Logger(level) {\n override fun write(message: String) = println("[Console] $message")\n}\n\nclass FileLogger(level: Int) : Logger(level) {\n override fun write(message: String) = println("[File] $message")\n}\n\nclass EmailLogger(level: Int) : Logger(level) {\n override fun write(message: String) = println("[Email] $message")\n}\n\n// Verwendung\nfun main() {\n val logger = ConsoleLogger(1).apply {\n setNext(FileLogger(2))\n .setNext(EmailLogger(3))\n }\n logger.log(2, "Warnung: niedriger Arbeitsspeicher")\n // Ausgabe:\n // [Console] Warnung: niedriger Arbeitsspeicher\n // [File] Warnung: niedriger Arbeitsspeicher\n}\n\nVorteile in Kotlin: Dank apply und der verketteten setNext-Aufrufe wirkt der Code prägnant. Die abstrakte Klasse kann auch durch ein Interface mit Standardimplementierung ersetzt werden.
2. Interpreter
\n\nZweck: Definiert eine Grammatik einer Sprache und interpretiert Sätze dieser Sprache.
\n\nProblem: Einfache arithmetische Ausdrücke (z. B. "3 + 5 - 2") müssen analysiert und berechnet werden.
\n\nImplementierung in Kotlin mit sealed class:
\n\n// Abstrakter Ausdruck\nsealed class Expression {\n abstract fun interpret(): Int\n}\n\n// Terminalausdruck – Zahl\ndata class Number(val value: Int) : Expression() {\n override fun interpret() = value\n}\n\n// Nichtterminalausdrücke – Operationen\ndata class Add(val left: Expression, val right: Expression) : Expression() {\n override fun interpret() = left.interpret() + right.interpret()\n}\n\ndata class Subtract(val left: Expression, val right: Expression) : Expression() {\n override fun interpret() = left.interpret() - right.interpret()\n}\n\n// Parser (vereinfacht)\nfun parse(tokens: List<String>): Expression {\n val stack = mutableListOf<Expression>()\n var i = 0\n while (i < tokens.size) {\n when (tokens[i]) {\n "+" -> {\n val right = Number(tokens[++i].toInt())\n val left = stack.removeLast()\n stack.add(Add(left, right))\n }\n "-" -> {\n val right = Number(tokens[++i].toInt())\n val left = stack.removeLast()\n stack.add(Subtract(left, right))\n }\n else -> stack.add(Number(tokens[i]