Принцип единой ответственности (SRP)
Суть
SRP — первая буква в SOLID: у модуля должна быть одна причина для изменения. Роберт Мартин уточняет: «причина» — это один заинтересованный актор (команда, отдел, роль). Если тип меняют и ради нового формата экрана, и ради смены API, и ради нового формата хранения — у него три ответственности.
Как выглядит нарушение в iOS
Классический Massive View Controller: контроллер ходит в сеть, парсит JSON, форматирует даты, настраивает ячейки и пишет в UserDefaults. Любая правка задевает всё, а тестировать можно только через UI.
Пример разделения
import Foundation
struct Order: Codable { let id: Int; let total: Decimal }
// Violation: one type loads, formats and persists — three reasons to change.
// Better: three small types with one responsibility each.
protocol OrderRepository { func orders() async throws -> [Order] }
struct OrderFormatter {
func title(for order: Order) -> String {
"Order #\(order.id): \(order.total)"
}
}
@MainActor @Observable
final class OrdersViewModel {
private(set) var titles: [String] = []
private let repository: any OrderRepository
private let formatter = OrderFormatter()
init(repository: any OrderRepository) { self.repository = repository }
func load() async {
let orders = (try? await repository.orders()) ?? []
titles = orders.map(formatter.title(for:))
}
}
OrderRepository отвечает за получение данных, OrderFormatter — за представление, OrdersViewModel — за состояние экрана. Каждый тестируется отдельно, репозиторий подменяется стабом.
На что смотрит интервьюер
- SRP — не «один метод на класс», а одна ось изменений. Переусердствовав, получаем десятки типов-однодневок; критерий — кто и зачем будет менять этот код.
- Связь с остальными принципами: SRP упрощает OCP (меньше поводов трогать класс) и DIP (маленькие зависимости легко инжектить через протоколы).
- Признаки нарушения: слово «And» в названии (
NetworkAndCacheManager), большойimport-список, тесты, которым нужно поднимать половину приложения.