Вопрос проверяет понимание абстракций в Swift и умение строить код, который легко расширять и тестировать.
Протоколы задают требования к типам: какие свойства и методы должны быть. Они помогают описывать поведение, а не конкретную реализацию. Это упрощает переиспользование кода и замену зависимостей. Протоколы часто применяются для архитектуры, тестирования и разделения ответственности. В Swift протоколы особенно важны, потому что их можно сочетать с расширениями и ограничениями.
Протоколы — один из ключевых инструментов Swift для построения гибкой архитектуры, потому что они позволяют описывать «что умеет объект», не привязываясь к тому, «что это за объект».
Протокол — это контракт, который описывает набор требований (методы, свойства, связанные типы), а конкретные типы могут ему соответствовать.
Перед примерами важно разделить две задачи: описание поведения и уменьшение связности.
Когда код работает с протоколом, ему не важно, какой именно класс или структура внутри, важны только возможности.
Пример:
protocol AnalyticsTracking {
func track(event: String)
}
Здесь любая реализация аналитики подходит, если умеет track.
Если экран напрямую зависит от конкретного сервиса, его сложно менять и тестировать. Протокол позволяет «развернуть» зависимость на уровень интерфейса.
Пример использования:
final class ProfileViewModel {
private let analytics: AnalyticsTracking
init(analytics: AnalyticsTracking) {
self.analytics = analytics
}
func onAppear() {
analytics.track(event: "profile_open")
}
}
В тестах можно подставлять заглушки и проверять поведение.
Пример заглушки:
final class AnalyticsSpy: AnalyticsTracking {
var events: [String] = []
func track(event: String) { events.append(event) }
}
Протоколы можно расширять, добавляя дефолтную реализацию.
Пример:
protocol IdentifiableView {
static var reuseId: String { get }
}
extension IdentifiableView {
static var reuseId: String { String(describing: Self.self) }
}
Теперь любые UITableViewCell, которые принимают протокол, получают reuseId без копипаста.
Можно собирать нужный набор возможностей через несколько протоколов, а не через сложное наследование.
Пример:
typealias CachableRequest = Encodable & Decodable
Сервисы: сеть, хранилище, аналитика.
Слой навигации: роутеры/координаторы.
UI-компоненты: конфигурация ячеек, обработка событий.
Архитектура: разделение слоев на контракты.
Протоколы стоит использовать, когда нужно отделить поведение от реализации: для тестируемости, расширяемости и снижения связности. Это один из самых сильных инструментов Swift для архитектуры.