Ответ
SOLID — это набор из пяти принципов объектно-ориентированного дизайна, направленных на создание поддерживаемого и расширяемого кода.
1. S (Single Responsibility Principle — Принцип единственной ответственности)
Класс должен иметь одну и только одну причину для изменения.
// НАРУШЕНИЕ: Класс занимается и логикой, и сохранением, и валидацией.
class UserManager {
func authenticate(user: String, pass: String) { /* ... */ }
func saveToDatabase(user: User) { /* ... */ }
func validateEmail(_ email: String) -> Bool { /* ... */ }
}
// СОБЛЮДЕНИЕ: Ответственности разделены.
class UserAuthenticator {
func authenticate(credentials: Credentials) -> Bool { /* ... */ }
}
class UserRepository {
func save(user: User) throws { /* ... */ }
func fetch(userId: String) -> User? { /* ... */ }
}
class EmailValidator {
func validate(_ email: String) -> Bool { /* ... */ }
}
2. O (Open/Closed Principle — Принцип открытости/закрытости)
Классы должны быть открыты для расширения, но закрыты для модификации.
protocol ReportGenerator {
func generateReport() -> String
}
class PDFReportGenerator: ReportGenerator {
func generateReport() -> String { return "PDF Report" }
}
class HTMLReportGenerator: ReportGenerator {
func generateReport() -> String { return "HTML Report" }
}
class ReportProcessor {
// Класс закрыт для модификации (не меняем его код),
// но открыт для расширения (можно добавить новый тип генератора).
func processReport(using generator: ReportGenerator) {
let report = generator.generateReport()
print("Processing: (report)")
}
}
3. L (Liskov Substitution Principle — Принцип подстановки Барбары Лисков)
Объекты базового класса должны быть заменяемы объектами подклассов без изменения корректности программы.
// Базовый класс и подклассы, которые его корректно заменяют.
protocol Shape {
var area: Double { get }
}
struct Rectangle: Shape {
let width, height: Double
var area: Double { width * height }
}
struct Circle: Shape {
let radius: Double
var area: Double { .pi * radius * radius }
}
// Функция работает с любым Shape, соблюдая LSP.
func printArea(of shape: Shape) {
print("Area is: (shape.area)")
}
4. I (Interface Segregation Principle — Принцип разделения интерфейса)
Клиенты не должны зависеть от методов, которые они не используют.
// НАРУШЕНИЕ: Многофункциональный протокол заставляет SimplePrinter реализовывать ненужный метод.
protocol MultiFunctionDevice {
func print(document: String)
func scan() -> Data
func fax(document: String)
}
// СОБЛЮДЕНИЕ: Протоколы разделены по функциям.
protocol Printable {
func print(document: String)
}
protocol Scannable {
func scan() -> Data
}
class SimplePrinter: Printable { // Реализует только нужное
func print(document: String) { /* ... */ }
}
class Photocopier: Printable, Scannable { // Реализует несколько ролей
func print(document: String) { /* ... */ }
func scan() -> Data { /* ... */ }
}
5. D (Dependency Inversion Principle — Принцип инверсии зависимостей)
Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций.
// Абстракция (протокол)
protocol DataStorage {
func save(data: Data, forKey key: String)
func loadData(forKey key: String) -> Data?
}
// Низкоуровневый модуль (деталь реализации)
class UserDefaultsStorage: DataStorage {
func save(data: Data, forKey key: String) { UserDefaults.standard.set(data, forKey: key) }
func loadData(forKey key: String) -> Data? { UserDefaults.standard.data(forKey: key) }
}
class KeychainStorage: DataStorage { /* ... */ }
// Высокоуровневый модуль зависит от абстракции, а не от конкретного хранилища.
class SettingsManager {
private let storage: DataStorage // Зависимость от протокола
// Внедрение зависимости через инициализатор (Dependency Injection)
init(storage: DataStorage = UserDefaultsStorage()) {
self.storage = storage
}
func saveSettings(_ settings: Settings) {
let data = try! JSONEncoder().encode(settings)
storage.save(data: data, forKey: "userSettings")
}
}