Вопрос проверяет умение применять протоколы для разделения слоев, внедрения зависимостей и повышения тестируемости.
Протоколы позволяют описать границы между модулями через контракты. Благодаря этому компоненты зависят не от конкретных классов, а от интерфейсов. Это облегчает замену реализаций, мокирование в тестах и развитие проекта. Протоколы помогают уменьшить связность и избежать «комбайнов» в контроллерах. В архитектурах вроде MVVM, VIPER и Coordinator протоколы — базовый строительный материал.
В архитектуре цель обычно одна: сделать код предсказуемым, расширяемым и тестируемым. Протоколы помогают этого достичь, потому что они позволяют явно задать контракты между частями приложения.
Перед разбором важно понимать: протоколы не «улучшают архитектуру сами по себе», они улучшают ее, когда применяются на границах ответственности.
Например, экран не должен знать, как именно устроен сетевой слой. Он должен знать только «дай мне данные».
Пример контракта репозитория:
protocol UserRepository {
func loadUser(id: String) async throws -> User
}
ViewModel зависит от UserRepository, а не от конкретного NetworkUserRepository.
Компонент верхнего уровня (UI) не зависит от деталей (конкретных сервисов), он зависит от абстракции (протокола).
Что это дает:
Можно заменить реализацию без переписывания UI.
Можно внедрять разные реализации для dev/prod.
Можно изолировать модуль.
С протоколом легко заменить зависимость на мок/спай и проверить поведение.
Пример мока:
final class UserRepositoryMock: UserRepository {
var result: Result<User, Error> = .failure(NSError())
func loadUser(id: String) async throws -> User {
try result.get()
}
}
Тест проверяет ViewModel, не трогая сеть.
В крупных проектах протоколы помогают ограничить публичную поверхность модуля.
Практика:
В модуле экспортируются только протоколы.
Конкретные классы остаются internal.
Это снижает связанность между фичами.
Одна из причин разрастания контроллера — он начинает «уметь все»: сеть, парсинг, хранение, навигацию. Протоколы помогают вынести это в отдельные сервисы с понятными контрактами.
Перед примером важно: цель — сделать зависимости направленными и контролируемыми.
UI слой:
ViewController / SwiftUI View
Presentation слой:
ViewModel / Presenter
Domain/Service слой:
UseCase / Interactor / Service
Data слой:
Repository / Storage / API
Между слоями:
протоколы как границы;
реализации “подключаются” через внедрение зависимостей.
Протоколы особенно полезны на границах слоев: UI ↔ бизнес-логика ↔ данные. Они уменьшают связность, упрощают тестирование и позволяют развивать проект без массовых переписываний.