Design Patterns in Kotlin: Advanced Techniques with Examples

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Design Patterns in Kotlin with Examples: Advanced Level

\n\n

Design patterns are proven solutions to common problems in software development. If you have already mastered basic patterns (Singleton, Factory, Observer), it's time to move to the next level. In this article, we will cover four advanced patterns — Chain of Responsibility, Interpreter, Mediator, and Memento — and show their implementation in Kotlin using language idioms (sealed class, extension functions, lambdas).

\n\n

1. Chain of Responsibility

\n\n

Purpose: Allows passing a request sequentially along a chain of handlers until one of them processes the request.

\n\n

Problem: A logging system where messages of different levels (DEBUG, INFO, ERROR) must be processed by different loggers.

\n\n

Implementation in Kotlin:

\n\n
// Abstract handler\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// Concrete handlers\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// Usage\nfun main() {\n    val logger = ConsoleLogger(1).apply {\n        setNext(FileLogger(2))\n            .setNext(EmailLogger(3))\n    }\n    logger.log(2, "Warning: low memory")\n    // Output:\n    // [Console] Warning: low memory\n    // [File] Warning: low memory\n}
\n\n

Advantages in Kotlin: Thanks to apply and the chained setNext call, the code is concise. You can also replace the abstract class with an interface using default implementation.

\n\n

2. Interpreter

\n\n

Purpose: Defines a grammar for a language and interprets sentences in that language.

\n\n

Problem: Need to parse and evaluate simple arithmetic expressions (e.g., "3 + 5 - 2").

\n\n

Implementation in Kotlin with sealed class:

\n\n
// Abstract expression\nsealed class Expression {\n    abstract fun interpret(): Int\n}\n\n// Terminal expression — number\ndata class Number(val value: Int) : Expression() {\n    override fun interpret() = value\n}\n\n// Non-terminal expressions — operations\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 (simplified)\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]                

Blogs

Book Recommendations