← Назад к вопросам

Приведи пример нарушения принципов SOLID

2.2 Middle🔥 182 комментариев
#Архитектура и паттерны

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Нарушение принципов SOLID в iOS-разработке

Рассмотрим классический пример нарушения SOLID-принципов в iOS-приложении — класс, отвечающий за загрузку и кеширование изображений.

Исходный проблемный код

class ImageManager {
    private var cache: [String: UIImage] = [:]
    
    func downloadImage(from url: URL, completion: @escaping (UIImage?) -> Void) {
        // Проверяем кеш
        if let cachedImage = cache[url.absoluteString] {
            completion(cachedImage)
            return
        }
        
        // Загружаем из сети
        URLSession.shared.dataTask(with: url) { data, _, error in
            guard let data = data, error == nil else {
                completion(nil)
                return
            }
            
            // Парсим изображение
            let image = UIImage(data: data)
            
            // Кешируем
            self.cache[url.absoluteString] = image
            
            // Сохраняем на диск
            self.saveToDisk(image: data, forKey: url.absoluteString)
            
            // Обновляем UI
            DispatchQueue.main.async {
                completion(image)
            }
        }.resume()
    }
    
    private func saveToDisk(image: Data, forKey key: String) {
        // Сложная логика сохранения в файловую систему
        let fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
            .appendingPathComponent(key)
        try? image.write(to: fileURL)
    }
}

Анализ нарушений SOLID

1. Нарушение Single Responsibility Principle (SRP)

Класс ImageManager выполняет слишком много обязанностей:

  • Управление кешем в памяти
  • Сетевая загрузка данных
  • Парсинг изображений
  • Сохранение на диск
  • Координация потоков (переход в main thread)

Это нарушение приводит к хрупкости класса — изменение любой из этих обязанностей потребует модификации одного монолитного объекта.

2. Нарушение Open-Closed Principle (OCP)

Класс не закрыт для изменений:

// Если мы захотим добавить другой тип кеша (например, LRU-кеш),
// придется изменять внутреннюю реализацию ImageManager
class LRUCache {
    // Новая реализация кеша
}

Мы не можем расширить функциональность без изменения существующего кода класса.

3. Нарушение Liskov Substitution Principle (LSP)

Принцип нарушается неявно, поскольку класс не предназначен для наследования:

class SpecialImageManager: ImageManager {
    override func downloadImage(from url: URL, completion: @escaping (UIImage?) -> Void) {
        // Нарушаем ожидаемое поведение родительского класса
        // Например, не вызываем completion в main thread
        super.downloadImage(from: url, completion: completion)
        // Нарушение: completion может быть вызван не в main thread
    }
}

Подклассы могут нарушать контракты базового класса.

4. Нарушение Interface Segregation Principle (ISP)

У класса нет интерфейсов, что приводит к:

  • Жесткой связанности с конкретной реализацией
  • Невозможности подменить зависимости
  • Сложностям при тестировании
// Клиентский код жестко зависит от конкретного класса
let manager = ImageManager()
manager.downloadImage(from: url) { image in
    // ...
}

5. Нарушение Dependency Inversion Principle (DIP)

Класс зависит от конкретных реализаций, а не абстракций:

  • URLSession.shared — конкретная синглтон-сессия
  • FileManager.default — конкретный файловый менеджер
  • UIImage — конкретный класс для работы с изображениями
// Прямая зависимость от конкретных классов
URLSession.shared.dataTask(with: url) { ... }
FileManager.default.urls(for: ...)
UIImage(data: data)

Последствия нарушений

  1. Сложность тестирования — невозможно изолированно протестировать загрузку без сети, кеширования или файловой системы
  2. Низкая переиспользуемость — код нельзя использовать в других контекстах
  3. Хрупкость изменений — модификация одной функции ломает другие
  4. Высокая связанность — компоненты системы тесно переплетены

Исправленный подход с соблюдением SOLID

// Протокол для кеширования (ISP, DIP)
protocol ImageCache {
    func image(for key: String) -> UIImage?
    func save(_ image: UIImage, for key: String)
}

// Протокол для загрузки (ISP, DIP)
protocol ImageLoader {
    func loadImage(from url: URL, completion: @escaping (Result<UIImage, Error>) -> Void)
}

// Класс с единственной ответственностью (SRP)
class NetworkImageLoader: ImageLoader {
    private let session: URLSession
    
    init(session: URLSession = .shared) {
        self.session = session // Внедрение зависимости (DIP)
    }
    
    func loadImage(from url: URL, completion: @escaping (Result<UIImage, Error>) -> Void) {
        // Только сетевая загрузка
    }
}

// Композиция вместо наследования (LSP)
class ImageService {
    private let cache: ImageCache
    private let loader: ImageLoader
    
    init(cache: ImageCache, loader: ImageLoader) {
        self.cache = cache
        self.loader = loader
    }
    
    func fetchImage(from url: URL, completion: @escaping (UIImage?) -> Void) {
        // Оркестрация компонентов
    }
}

Такой подход позволяет легко заменять компоненты, изолированно тестировать каждый модуль и расширять функциональность без модификации существующего кода, что соответствует всем пяти принципам SOLID.

Приведи пример нарушения принципов SOLID | PrepBro