Kotlin: Функциональный или ООП? Выбираем подход правильно

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Kotlin: Два мира в одном языке

Вы когда-нибудь задумывались, почему одни разработчики пишут на Kotlin как на улучшенной Java, а другие превращают код в каскад лямбд и цепочек вызовов? Ответ прост: Kotlin — это мультипарадигмальный язык, который одинаково хорошо дружит и с объектно-ориентированным программированием (ООП), и с функциональным программированием (ФП).

Но как новичку не потеряться в этом разнообразии? А опытному разработчику — не скатиться в «спагетти-код» из-за смешения стилей? В этой статье мы разберем, чем отличаются эти подходы, где и какой применять, и как Kotlin помогает объединить их лучшие стороны. Готовьтесь: будет много кода и никакой скуки.

Если вы изучаете Kotlin для Android-разработки или бэкенда, понимание этих парадигм — ваш билет в мир чистого, читаемого и поддерживаемого кода. Поехали!

Объектно-ориентированный Kotlin: Классика в современной обертке

Объектно-ориентированный подход — это про сущности. У вас есть классы, у классов есть свойства и методы. Всё, что делает программа — это взаимодействие объектов. Kotlin берет лучшее из Java, но добавляет сахар: data class, object, ленивые свойства и многое другое.

Классы и наследование: пример

Представьте, что мы пишем систему для онлайн-магазина. ООП-подход будет выглядеть так:

// Базовый класс
open class Product(val name: String, val price: Double) {
    open fun getDescription(): String {
        return "$name стоит $price руб."
    }
}

// Наследование
class DiscountedProduct(name: String, price: Double, val discount: Double) : Product(name, price) {
    override fun getDescription(): String {
        val finalPrice = price * (1 - discount / 100)
        return "$name со скидкой $discount% стоит ${"%.2f".format(finalPrice)} руб."
    }
}

fun main() {
    val apple = Product("Яблоко", 50.0)
    val saleApple = DiscountedProduct("Яблоко", 50.0, 20.0)
    
    println(apple.getDescription())  // Яблоко стоит 50.0 руб.
    println(saleApple.getDescription()) // Яблоко со скидкой 20% стоит 40.00 руб.
}

Здесь всё логично: есть иерархия, полиморфизм, инкапсуляция. Но есть и минусы: код становится тяжёлым, если классов много. А если нам нужно просто применить скидку к любому товару — придётся плодить подклассы.

Когда ООП рулит?

  • Сложные бизнес-модели — когда сущности имеют множество состояний и поведений.
  • UI-компоненты — Activity, Fragment, View — всё это классическое ООП.
  • Работа с базами данных — ORM (например, Room) использует рефлексию и аннотации, завязанные на классах.

Функциональный Kotlin: Лаконичность и безопасность

Функциональное программирование — это про вычисления, а не про состояния. Вместо мутации объектов мы используем неизменяемые данные и чистые функции. Kotlin не заставляет вас писать в функциональном стиле, но предоставляет для этого все инструменты: лямбды, функции высшего порядка, монадоподобные конструкции (например, Result).

Функции высшего порядка: пример

Давайте решим ту же задачу со скидками, но функционально:

data class Product(val name: String, val price: Double)

fun applyDiscount(discount: Double): (Product) -> Product = { product ->
    val finalPrice = product.price * (1 - discount / 100)
    product.copy(price = finalPrice)
}

fun main() {
    val apple = Product("Яблоко", 50.0)
    val saleApple = applyDiscount(20.0)(apple)
    
    println("${apple.name} стоит ${apple.price} руб.")           // Яблоко стоит 50.0 руб.
    println("${saleApple.name} со скидкой стоит ${saleApple.price} руб.") // Яблоко со скидкой стоит 40.0 руб.
}

Обратите внимание: мы не создаём новый класс. Мы просто передаём данные через функцию. data class даёт нам copy(), который делает объект неизменяемым. Это ключевая фишка ФП в Kotlin.

Лямбды и цепочки: магия коллекций

Функциональный стиль особенно хорош при работе с коллекциями. Сравните:

// Императивный (ООП-стиль)
val numbers = listOf(1, 2, 3, 4, 5)
val evenNumbers = mutableListOf<Int>()
for (n in numbers) {
    if (n % 2 == 0) {
        evenNumbers.add(n * 2)
    }
}
println(evenNumbers) // [4, 8]

// Функциональный стиль
val numbers2 = listOf(1, 2, 3, 4, 5)
val result = numbers2
    .filter { it % 2 == 0 }
    .map { it * 2 }
println(result) // [4, 8]

Второй вариант короче, понятнее и безопаснее (нет мутабельного списка). Kotlin-разработчики обожают такие цепочки.

Когда ФП спасает?

  • Обработка данных — трансформация списков, работа с API, парсинг JSON.
  • Асинхронность — корутины и Flow построены на функциональных принципах.
  • Тестирование — чистые функции легко тестировать без моков.

Битва парадигм: что выбрать в 2024?

Спойлер: вам не нужно выбирать. Kotlin создан для того, чтобы вы смешивали подходы. Но есть правила хорошего тона.

Смешиваем правильно

Представьте, что вы пишете приложение для заметок. ООП подойдёт для модели данных и UI-слоя. А функциональный подход — для фильтрации, поиска и сортировки заметок. Пример:

// ООП: модель
data class Note(val id: Int, val title: String, val content: String, val isFavorite: Boolean)

class NoteRepository {
    private val notes = mutableListOf<Note>()
    
    fun add(note: Note) = notes.add(note)
    fun getAll() = notes.toList()
}

// ФП: бизнес-логика
fun List<Note>.search(query: String): List<Note> {
    return this.filter { 
        it.title.contains(query, ignoreCase = true) || 
        it.content.contains(query, ignoreCase = true) 
    }
}

fun List<Note>.favorites(): List<Note> = this.filter { it.isFavorite }

fun main() {
    val repo = NoteRepository()
    repo.add(Note(1, "Kotlin", "Изучаю функциональное программирование", true))
    repo.add(Note(2, "Python", "Надо обновить библиотеки", false))
    
    val result = repo.getAll()
        .search("Kotlin")
        .favorites()
    
    println(result) // [Note(id=1, title=Kotlin, ...)]
}

                
                

Блоги

Рекомендаций по книгам