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

«Какие классы UIKit часто нарушают принципы SOLID?» — вопрос из категории ООП, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Многие классы 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, но логически проблема остаётся).