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

Приведи пример использования принципов SOLID

2.0 Middle🔥 182 комментариев
#Архитектура и паттерны#Язык Swift

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

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

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

Пример применения принципов SOLID в iOS-разработке

Рассмотрим разработку сервиса для загрузки и обработки изображений, который будет соответствовать всем пяти принципам SOLID.

1. Single Responsibility Principle (SRP) - Принцип единственной ответственности

Каждый класс должен иметь только одну причину для изменения. Вместо монолитного класса, который делает всё, разделим ответственности:

// Класс только загружает данные
class ImageDownloader {
    func downloadImage(from url: URL, completion: @escaping (Data?) -> Void) {
        URLSession.shared.dataTask(with: url) { data, _, _ in
            completion(data)
        }.resume()
    }
}

// Класс только кэширует изображения
class ImageCache {
    private var cache = NSCache<NSString, UIImage>()
    
    func cacheImage(_ image: UIImage, for key: String) {
        cache.setObject(image, forKey: key as NSString)
    }
    
    func getImage(for key: String) -> UIImage? {
        cache.object(forKey: key as NSString)
    }
}

// Класс только обрабатывает изображения
class ImageProcessor {
    func resizeImage(_ image: UIImage, to size: CGSize) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
        image.draw(in: CGRect(origin: .zero, size: size))
        let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return resizedImage ?? image
    }
}

2. Open/Closed Principle (OCP) - Принцип открытости/закрытости

Классы должны быть открыты для расширения, но закрыты для модификации. Используем протоколы для поддержки новых форматов:

protocol ImageFilter {
    func apply(to image: UIImage) -> UIImage
}

class GrayscaleFilter: ImageFilter {
    func apply(to image: UIImage) -> UIImage {
        // Реализация конвертации в оттенки серого
        return image
    }
}

class BlurFilter: ImageFilter {
    private let radius: CGFloat
    
    init(radius: CGFloat) {
        self.radius = radius
    }
    
    func apply(to image: UIImage) -> UIImage {
        // Реализация размытия
        return image
    }
}

class ImageFilterManager {
    private var filters: [ImageFilter] = []
    
    func addFilter(_ filter: ImageFilter) {
        filters.append(filter)
    }
    
    func applyFilters(to image: UIImage) -> UIImage {
        var result = image
        for filter in filters {
            result = filter.apply(to: result)
        }
        return result
    }
}

3. Liskov Substitution Principle (LSP) - Принцип подстановки Барбары Лисков

Наследующие классы должны дополнять, а не изменять поведение базового класса:

protocol ImageSource {
    func loadImage(completion: @escaping (UIImage?) -> Void)
}

class NetworkImageSource: ImageSource {
    private let url: URL
    
    init(url: URL) {
        self.url = url
    }
    
    func loadImage(completion: @escaping (UIImage?) -> Void) {
        ImageDownloader().downloadImage(from: url) { data in
            guard let data = data else {
                completion(nil)
                return
            }
            completion(UIImage(data: data))
        }
    }
}

class LocalImageSource: ImageSource {
    private let imageName: String
    
    init(imageName: String) {
        self.imageName = imageName
    }
    
    func loadImage(completion: @escaping (UIImage?) -> Void) {
        completion(UIImage(named: imageName))
    }
}

// Клиентский код работает с любым ImageSource
class ImageLoader {
    func loadImage(from source: ImageSource, completion: @escaping (UIImage?) -> Void) {
        source.loadImage(completion: completion)
    }
}

4. Interface Segregation Principle (ISP) - Принцип разделения интерфейсов

Лучше много специализированных интерфейсов, чем один общий:

// Вместо одного "жирного" протокола:
// protocol ImageManager {
//     func download()
//     func cache()
//     func resize()
//     func applyFilter()
// }

// Разделяем на специализированные протоколы:
protocol Downloadable {
    func download(from url: URL, completion: @escaping (Data?) -> Void)
}

protocol Cacheable {
    associatedtype Key
    func save(_ data: Data, for key: Key)
    func get(for key: Key) -> Data?
}

protocol Resizable {
    func resize(to size: CGSize) -> UIImage
}

class ModernImageService: Downloadable, Cacheable {
    typealias Key = String
    
    func download(from url: URL, completion: @escaping (Data?) -> Void) {
        // Реализация загрузки
    }
    
    func save(_ data: Data, for key: String) {
        // Реализация сохранения
    }
    
    func get(for key: String) -> Data? {
        // Реализация получения
        return nil
    }
}

5. Dependency Inversion Principle (DIP) - Принцип инверсии зависимостей

Зависимости должны строиться на абстракциях, а не на конкретных реализациях:

protocol ImageRepository {
    func getImage(for id: String, completion: @escaping (UIImage?) -> Void)
}

class ImageViewController: UIViewController {
    private let imageRepository: ImageRepository
    private let imageView = UIImageView()
    
    // Внедряем зависимость через инициализатор
    init(repository: ImageRepository) {
        self.imageRepository = repository
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func loadImage(with id: String) {
        imageRepository.getImage(for: id) { [weak self] image in
            DispatchQueue.main.async {
                self?.imageView.image = image
            }
        }
    }
}

// Реализации для разных источников
class NetworkImageRepository: ImageRepository {
    func getImage(for id: String, completion: @escaping (UIImage?) -> Void) {
        // Загрузка из сети
    }
}

class LocalImageRepository: ImageRepository {
    func getImage(for id: String, completion: @escaping (UIImage?) -> Void) {
        // Загрузка из локального хранилища
    }
}

// Использование с dependency injection
let viewController = ImageViewController(repository: NetworkImageRepository())

Преимущества SOLID в iOS-разработке:

Тестируемость - Каждый компонент можно тестировать изолированно:

class MockImageRepository: ImageRepository {
    var mockImage: UIImage?
    
    func getImage(for id: String, completion: @escaping (UIImage?) -> Void) {
        completion(mockImage)
    }
}

Гибкость - Легко заменять реализации без изменения клиентского кода.

Поддерживаемость - Упрощается рефакторинг и добавление нового функционала.

Масштабируемость - Новые разработчики быстрее понимают код благодаря четкой структуре.

Применение SOLID в iOS-разработке особенно важно при создании долгоживущих приложений с постоянно растущей кодобазой. Эти принципы помогают создавать архитектуру, которая устойчива к изменениям требований и упрощает командную разработку. Современные архитектурные подходы (MVVM, VIPER, Clean Architecture) активно используют эти принципы в своей основе.

Приведи пример использования принципов SOLID | PrepBro