Функциональный или ООП в Swift: что выбрать для iOS-проекта?

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

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

Начать курс

Две стороны одной медали: Функциональный vs объектно-ориентированный подход в Swift

Когда я только начинал писать под iOS, мне казалось, что объектно-ориентированное программирование — это единственный правильный путь. Классы, наследование, полиморфизм — всё это звучало как священное писание для разработчика. Но потом я открыл для себя функциональное программирование в Swift, и мир перевернулся. Оказывается, можно писать код, который не мутирует состояние, который легко тестировать и который выглядит как математическая формула.

Swift — уникальный язык. Он мультипарадигменный. Это значит, что вы можете смешивать ООП и функциональный подход в одном проекте, брать лучшее из двух миров. Но как не запутаться? Когда использовать классы, а когда — замыкания и map? Давайте разбираться.

Объектно-ориентированный Swift: классика, которая работает

Объектно-ориентированное программирование (ООП) в Swift строится вокруг классов, протоколов и наследования. Это парадигма, где данные и поведение живут вместе. Вы создаёте модель — например, «Пользователь» — и этот объект сам знает, как представиться, как изменить имя или посчитать возраст.

Вот классический пример на Swift:

class User {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func greet() -> String {
        return "Привет, меня зовут \(name) и мне \(age) лет."
    }
    
    func haveBirthday() {
        age += 1
    }
}

let user = User(name: "Анна", age: 25)
user.haveBirthday()
print(user.greet()) // Привет, меня зовут Анна и мне 26 лет.

В этом коде мы используем объектно-ориентированный подход в Swift. У нас есть состояние (свойства name, age), которое может меняться (метод haveBirthday). Это интуитивно понятно, особенно если вы пришли из Java или C++. Но есть нюанс: мутабельное состояние — источник багов. Чем больше объектов меняют друг друга, тем сложнее отлаживать программу.

Функциональный Swift: сила замыканий и неизменяемости

Функциональное программирование в Swift предлагает другой взгляд. Здесь функции — это «граждане первого класса». Вы можете передавать их как параметры, возвращать из других функций и хранить в переменных. Главный принцип — чистые функции (pure functions), которые не меняют внешнее состояние и всегда возвращают одинаковый результат для одинаковых входных данных.

Посмотрим, как тот же пример выглядит в функциональном стиле:

struct User {
    let name: String
    let age: Int
}

func greet(user: User) -> String {
    return "Привет, меня зовут \(user.name) и мне \(user.age) лет."
}

func haveBirthday(user: User) -> User {
    return User(name: user.name, age: user.age + 1)
}

let user = User(name: "Анна", age: 25)
let olderUser = haveBirthday(user: user)
print(greet(user: olderUser)) // Привет, меня зовут Анна и мне 26 лет.

Обратите внимание: мы используем структуру (struct), а не класс. Структуры в Swift — это value types. Они копируются при присваивании. Функция haveBirthday не мутирует исходный объект, а создаёт новый. Это ключевая идея функционального подхода — неизменяемость (immutability).

Сравнение подходов: когда что использовать?

Теперь давайте честно сравним ООП и функциональное программирование в Swift. У каждого есть свои сильные стороны.

Объектно-ориентированный подход в Swift

  • Плюсы: Интуитивно понятен для моделирования реального мира. Отлично подходит для UI-компонентов (UIViewController, UIView). Наследование упрощает переиспользование кода.
  • Минусы: Мутабельное состояние усложняет тестирование и отладку. Большие иерархии классов становятся «хрупкими» — изменение в базовом классе может сломать всё.
  • Когда использовать: Для объектов с долгой жизнью (синглтоны, координаторы), для UIKit/AppKit компонентов, когда вам нужна ссылочная семантика (reference types).

Функциональный подход в Swift

  • Плюсы: Код предсказуем и легко тестируется. Чистые функции не имеют побочных эффектов. Композиция функций позволяет строить сложную логику из простых блоков.
  • Минусы: Может быть сложен для новичков. Иногда приводит к избыточному копированию данных (но Swift оптимизирует copy-on-write). Не всегда удобен для UI.
  • Когда использовать: Для обработки данных (массивы, словари), для бизнес-логики, для асинхронных операций (Combine, RxSwift), для state management.

Реальный пример: фильтрация и трансформация данных

Давайте посмотрим, как эти парадигмы проявляются в реальной задаче. Допустим, у нас есть массив пользователей, и нам нужно получить имена всех совершеннолетних, отсортированные по возрасту.

ООП стиль:

class UserManager {
    private var users: [User]
    
    init(users: [User]) {
        self.users = users
    }
    
    func getAdultNamesSorted() -> [String] {
        var adults: [User] = []
        for user in users {
            if user.age >= 18 {
                adults.append(user)
            }
        }
        // Сортировка пузырьком для наглядности
        for i in 0..<adults.count {
            for j in 0..<adults.count - i - 1 {
                if adults[j].age > adults[j+1].age {
                    let temp = adults[j]
                    adults[j] = adults[j+1]
                    adults[j+1] = temp
                }
            }
        }
        var names: [String] = []
        for adult in adults {
            names.append(adult.name)
        }
        return names
    }
}

Функциональный стиль:

func getAdultNamesSorted(users: [User]) -> [String] {
    return users
        .filter { $0.age >= 18 }
        .sorted { $0.age < $1.age }
        .map { $0.name }
}

let names = getAdultNamesSorted(users: allUsers)
print(names) // ["Мария", "Анна", "Иван"]

Чувствуете разницу? Второй вариант читается как предложение: «возьми пользователей, отфильтруй взрослых, отсортируй по возрасту, получи имена». Это и есть функциональное программирование в Swift в действии. Код компактный, декларативный и без единого мутабельного состояния.

Мультипарадигменный Swift: как объединить подходы

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

Блоги

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