Сегодня мы разберём 5 принципов SOLID, которые спасут ваш код от хаоса.
🤔 Что такое SOLID?
Это 5 принципов объектно-ориентированного дизайна, которые делают код:
🔸 Гибким к изменениям.
🔸 Понятным для других разработчиков.
🔸 Более тестируемым.
Акроним расшифровывается так:
1️⃣ S — Single Responsibility.
Один класс — одна зона ответственности. Пример:
// Неправильно. Класс делает все: логика, UI, сетевые запросы.
class ProfileViewController: UIViewController {
func loadUserData() {
URLSession.shared.dataTask(...) { [weak self] data in
let user = decode(data)
self?.nameLabel.text = user.name
self?.avatarImageView.image = UIImage(data: user.avatarData)
}.resume()
}
}
// Правильно. Разделяем ответственность.
class UserLoader {
func fetchUser() async throws -> User {
// Запросы к бд или серверу и логика обработки данных
}
}
class ProfileViewController: UIViewController {
private let loader = UserLoader()
func updateUI() async {
let user = try? await loader.fetchUser()
if let user {
nameLabel.text = user.name
avatarImageView.image = UIImage(data: user.avatarData)
}
}
}
2️⃣ O — Open-Closed.
Класс открыт для расширения, но закрыт для изменений. Пример:
// Неправильно. При добавлении нового типа ячейки придётся менять метод.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = data[indexPath.row]
if item is Photo {
return PhotoCell()
} else if item is Video {
return VideoCell() // Добавили новое условие
}
}
// Правильно. Один из вариантов правильного решения - решение через протокол. Новый тип ячейки = новая сущность, а не правки в коде.
protocol CellConfigurable {
static var reuseID: String { get }
func configure(with item: Any)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = data[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: type(of: item).reuseID)
(cell as? CellConfigurable)?.configure(with: item)
return cell
}
3️⃣ L — Liskov Substitution.
Подклассы должны заменять родительские классы. Пример:
// Неправильно
class Bird {
func fly() {
print("Лететь!")
}
}
class Penguin: Bird {
override func fly() {
fatalError("Пингвины не летают!") // Ошибка
}
}
// Правильно
protocol Bird {
func move()
}
class Sparrow: Bird {
func move() {
print("Лететь!")
}
}
class Penguin: Bird {
func move() {
print("Плыть!")
}
}
4️⃣ I — Interface Segregation.
Клиенты не должны зависеть от интерфейсов, которые они не используют. Пример:
// Неправильно
protocol TableViewDelegate {
func didSelectRow(at indexPath: IndexPath)
func heightForRow(at indexPath: IndexPath) -> CGFloat
func willDisplayCell()
// ... 20+ методов
}
// Правильно: разделяем на мелкие протоколы, которые можно реализовывать при необходимости.
protocol TableViewSelectable {
func didSelectRow(at indexPath: IndexPath)
}
protocol TableViewSizable {
func heightForRow(at indexPath: IndexPath) -> CGFloat
}
5️⃣ D — Dependency Inversion.
Зависите от абстракций, а не от конкретных типов. Пример:
// Неправильно. Прямая зависимость от Firebase.
class AnalyticsService {
private let firebase = FirebaseAnalytics()
func track(event: String) {
firebase.log(event)
}
}
// Правильно. Гибкое решение.
protocol AnalyticsEngine {
func log(event: String)
}
class AnalyticsService {
private let engine: AnalyticsEngine // Зависим от протокола
init(engine: AnalyticsEngine) {
self.engine = engine
}
func track(event: String) {
engine.log(event)
}
}
💡 Вывод:
SOLID не просто теория, а практический инструмент для создания профессионального кода. Он требует больше времени на старте, но окупается с лихвой, так как упростит масштабируемость и тестирование проекта, позволит любому разработчику быстрее вникнуть в ваш код. Это как строить дом с фундаментом вместо хижины на песке.
➡️ Подписаться на канал
Мобильный трудоголик