Какие классы UIKit часто нарушают принципы SOLID?

Ответ

Многие классы UIKit, созданные в эпоху MVC, демонстрируют нарушения SOLID из-за высокой связности и многофункциональности.

1. UIViewController — нарушение SRP (Принцип единственной ответственности)

  • Проблема: «Massive View Controller» совмещает в себе:
    • Управление жизненным циклом view.
    • Бизнес-логику и обработку данных.
    • Навигацию и логику переходов.
    • Работу с делегатами таблиц/коллекций.
  • Решение: Выделение ответственностей в отдельные объекты (Presenter, Interactor, Router, ViewModel).

2. UITableViewDataSource / UICollectionViewDataSource — нарушение OCP (Принцип открытости/закрытости)

  • Проблема: Для изменения логики отображения ячеек (например, другой порядок секций) необходимо изменять код существующих методов numberOfSections, cellForRowAt.
  • Решение: Использование архитектурных подходов (Compositional Layout, Diffable Data Source) или паттерна «Спецификация» для инкапсуляции логики секций и ячеек.

3. Неявные зависимости — нарушение DIP (Принцип инверсии зависимостей)

  • Проблема: Прямое использование синглтонов и глобальных объектов.
    class ProfileViewController: UIViewController {
    func saveUser() {
        // Нарушение DIP: прямая зависимость от конкретной реализации (CoreData)
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
        // ...
    }
    }
  • Решение: Внедрение зависимости через протокол (интерфейс).
    
    protocol DatabaseService {
    func save(user: User)
    }

class ProfileViewController { let database: DatabaseService // Зависимость от абстракции init(database: DatabaseService) { self.database = database } }



**4. Крупные делегаты — нарушение ISP (Принцип разделения интерфейсов)**
*   **Проблема:** Протокол `UIScrollViewDelegate` содержит множество методов (`scrollViewDidScroll`, `scrollViewWillBeginDragging` и т.д.). Класс вынужден подписываться на весь протокол, даже если нужен один метод.
*   **Решение:** Разделение на более мелкие протоколы (на практике достигается через необязательные методы в Objective-C, но логически проблема остаётся).

Ответ 18+ 🔞

Ну слушай, вот смотри, эти старые UIKit-классы — они же как дед в деревне, который и печку топит, и корову доит, и забор чинит, и ещё в карты играет. Один на всё село, блядь. И вроде работает, но если он свалится — пиздец всему хозяйству. Так и тут.

1. UIViewController — это же просто ёбаный монстр, нарушение SRP в чистом виде

  • В чём пиздец: Этот типок пытается быть всем сразу. Он как швейцарский нож, который и отвертка, и пила, и открывалка, но когда им пытаешься консервы открыть — хуй сломаешь. Он и вьюшкой рулит, и данные тащит, и по экранам прыгает, и таблицы настраивает. Массивный View Controller, сука! Одна ответственность? Да их там овердохуища!
  • Что делать: Разделять этого франкенштейна. Вынести бизнес-логику в Interactor, отображение данных в Presenter или ViewModel, навигацию в Router. Пусть каждый занимается своим ебаным делом.

2. UITableViewDataSource — классическое нарушение OCP, ёпта

  • В чём пиздец: Хочешь поменять логику секций или ячеек? Добро пожаловать в ад, блядь. Лезешь в методы numberOfSections и cellForRowAt и начинаешь там костыли пилить. Код закрыт для расширения, но открыт для модификации — ровно наоборот, сука, от того, что нужно.
  • Что делать: Использовать Compositional Layout и Diffable Data Source — они уже более умные. Или паттерн «Спецификация», чтобы логику секций и ячеек инкапсулировать в отдельные объекты. Не лезть каждый раз в кишки старого кода.

3. Неявные зависимости — это пиздец, нарушение DIP

  • В чём пиздец: Прямое обращение к синглтонам и глобальным объектам — это как ходить по охуенно тонкому льду. Смотри, какой ужас:
class ProfileViewController: UIViewController {
    func saveUser() {
        // Вот она, зависимость, блядь! Прямой выстрел в колено.
        // Ты привязан к AppDelegate и CoreData намертво. Хуй отрефакторишь.
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
        // ...
    }
}
  • Что делать: Внедрять зависимости через протоколы, ёбана! Зависить от абстракции, а не от конкретной реализации.
protocol DatabaseService {
    func save(user: User)
}

class ProfileViewController {
    let database: DatabaseService // А вот так — красиво. Зависим от интерфейса.
    init(database: DatabaseService) { self.database = database } // Инжектим через инит.
}

Теперь можно подсунуть любую базу данных, хоть CoreData, хоть Realm, хоть файлик — контроллеру похуй.

4. Крупные делегаты — нарушение ISP, ебать мои старые костыли

  • В чём пиздец: Берём UIScrollViewDelegate. Методов там — дохуя. А тебе нужен только один, scrollViewDidScroll. Но подписываешься ты на весь протокол, и Xcode тебе подсказывает все остальные 15 методов, которые тебе нахуй не сдались. Интерфейс не разделён, ты вынужден тащить на себе весь этот груз.
  • Что делать: В Objective-C спасали необязательные методы, но логически-то проблема остаётся. Хорошая практика — выносить логику делегата в отдельный объект, который реализует только нужные методы. Или использовать современные замыкания (closures) там, где это возможно. Не городить огород из ненужного кода.

Короче, UIKit — это как старый, проверенный Запорожец. Едет, гремит, но чтобы сделать из него нормальную тачку, нужно половину узлов выкинуть и переделать. SOLID-принципы — это как инструкция по этому апгрейду.