Добавить объявление

Принципы SOLID в Swift: полный гид с примерами

Сегодня мы разберем 5 принципов SOLID, которые спасут ваш код от хаоса.

Что такое SOLID?


Это 5 принципов объектно-ориентированного дизайна, которые делают код:

  • Гибким к изменениям.

  • Понятным для других разработчиков.

  • Более тестируемым.


Акроним расшифровывается так:

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)
}
}
}

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
}

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("Плыть!")
}
}

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
}

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 не просто теория, а практический инструмент для создания профессионального кода. Он требует больше времени на старте, но окупается с лихвой, так как упростит масштабируемость и тестирование проекта, позволит любому разработчику быстрее вникнуть в ваш код. Это как строить дом с фундаментом вместо хижины на песке.
29.07.2025 57 410