Почему стоит использовать протоколы для сервисов
Что проверяют
Понимаете ли вы, зачем сервисному слою нужен контракт, и отличаете ли протокол как инструмент инверсии зависимостей от протокола, написанного потому что так принято. Попутно смотрят, как вы пишете тесты: без подмены сетевого слоя их обычно просто нет.
Как отвечать
Сервис это то, что ходит наружу: сеть, база, связка ключей, аналитика. Если экран держит конкретный NetworkService, он намертво связан с URLSession, а тест на него требует живого бэкенда. Протокол разрывает эту связь:
protocol ProfileLoading {
func profile(id: String) async throws -> Profile
}
final class ProfileViewModel {
private let service: ProfileLoading
init(service: ProfileLoading) { self.service = service }
}Что это даёт на практике:
- тесты: в
initподставляется заглушка, которая возвращает заранее заданный ответ или бросает ошибку, и можно проверить состояние экрана при отказе сети; - подмена реализации: кэширующая обёртка, версия с фиктивными данными для превью и UI-тестов;
- граница модуля: слой выше знает только объявленное в протоколе и не зависит от деталей запроса;
- композиция вместо наследования: тип может соответствовать нескольким протоколам, а расширения протоколов дают реализацию по умолчанию.
Важно добавить, что сам по себе протокол не решает ничего, если зависимость создаётся внутри потребителя. Контракт работает только вместе с инъекцией: объект получает сервис снаружи и не знает, откуда тот взялся.
Где спотыкаются
Заводят протокол на каждый класс, один в один повторяя его методы. Это не абстракция, а второй файл, который надо править вместе с первым. Протокол оправдан там, где есть вторая реализация, хотя бы тестовая.
Второе: объявляют протокол с associatedtype или требованием Self и пытаются хранить его в обычном свойстве. Такой протокол сам по себе типом значения не становится, нужны дженерики или стирание типа.
Третье: описывают протоколом делегат таблицы и считают, что ответили. Вопрос про сервисный слой и владение зависимостями, а UITableViewDelegate это про обратный вызов представления.