Абстракция
Суть
Абстракция — выделение существенного и сокрытие деталей. В коде это значит: работать с контрактом («умеет сохранять токен»), а не с конкретной реализацией («пишет в Keychain через SecItemAdd»). Так код зависит от стабильного интерфейса, а реализацию можно менять, подменять и тестировать отдельно.
В Swift главный инструмент абстракции — протокол. Абстрактных классов в языке нет; их роль выполняют протоколы с default-реализацией в extension.
Пример: абстракция хранилища
protocol TokenStorage {
func save(_ token: String) throws
func load() throws -> String?
}
struct KeychainStorage: TokenStorage { // прод: Security.framework
func save(_ token: String) throws { /* SecItemAdd ... */ }
func load() throws -> String? { nil /* SecItemCopyMatching ... */ }
}
final class InMemoryStorage: TokenStorage { // тесты: без Keychain
private var token: String?
func save(_ token: String) throws { self.token = token }
func load() throws -> String? { token }
}
final class AuthService {
private let storage: any TokenStorage // зависим от абстракции
init(storage: any TokenStorage) { self.storage = storage }
func logIn(token: String) throws { try storage.save(token) }
}
AuthService ничего не знает о Keychain. В тестах подставляем InMemoryStorage, в проде — KeychainStorage, а завтра — хранилище на сервере. Это dependency inversion: модуль высокого уровня зависит от протокола, а не от деталей.
Уровни абстракции в Swift
- Протоколы — контракт поведения;
some Protocol/any Protocol— способы на него ссылаться. - Generics — абстракция над типом:
func max<T: Comparable>работает с любым сравнимым типом. - Типы и модули —
publicAPI модуля скрываетinternal-реализацию. - Функции — самая простая абстракция: имя вместо последовательности шагов.
Отличие от инкапсуляции
Абстракция — про интерфейс: что видно снаружи. Инкапсуляция — про сокрытие: private и границы доступа, которые не дают обойти интерфейс. Они работают вместе: протокол задаёт абстракцию, private защищает реализацию.
На что смотрит интервьюер
Не определение «из учебника», а умение показать протокол + две реализации + инъекцию зависимости и объяснить, зачем это нужно (тестируемость, замена реализации, изоляция от сторонних SDK).