Инверсия зависимостей с абстракциями протоколов
Зависите от абстракций, а не от конкретных реализаций, используя протоколы Swift как границы.
«Инверсия зависимостей с абстракциями протоколов» — бесплатный урок Swift Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Swift Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Swift Academy содержит 4 уроков всего.
Принцип инверсии зависимостей
Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба уровня должны зависеть от абстракций (протоколов).
// WITHOUT DIP:
class OrderService { let db = SQLiteDatabase() } // depends on concrete
// WITH DIP:
class OrderService { let db: Database } // depends on abstractionОпределение абстракции
Выделите протокол, описывающий контракт, необходимый вашему модулю высокого уровня.
protocol Database {
func save<T: Encodable>(_ value: T, key: String) throws
func load<T: Decodable>(key: String) throws -> T
}Конкретные реализации
Предоставьте несколько конкретных реализаций за протоколом: рабочую, тестовую и будущие альтернативы.
class SQLiteDatabase: Database { ... }
class InMemoryDatabase: Database { ... }
class KeychainDatabase: Database { ... }Внедрение через инициализатор
Передавайте абстракцию через инициализатор — это самый явный и удобный для тестирования способ внедрения.
class ProfileService {
private let db: Database
init(db: Database) { self.db = db }
}Почему не стоит использовать синглтоны повсюду?
Глобальные синглтоны скрывают зависимости, из-за чего код трудно тестировать и анализировать.
// Hard to test:
class Service { func run() { MySingleton.shared.doWork() } }
// Easy to test:
class Service { let dep: MyProtocol; func run() { dep.doWork() } }Абстракции протоколов в Swift
Протоколы Swift могут содержать связанные типы, реализации по умолчанию и композицию, что делает их идеальными для выразительных абстракций.
protocol Cacheable {
associatedtype Key: Hashable
associatedtype Value
func get(_ key: Key) -> Value?
func set(_ key: Key, value: Value)
}Гранулярность абстракций
Разделение интерфейсов: предпочитайте узкие протоколы громоздким, чтобы реализации предоставляли только необходимое вызывающему коду.
// Fat (avoid):
protocol DataStore {
func save(...); func load(...); func delete(...); func query(...); func migrate()
}
// Narrow (prefer):
protocol Readable { func load<T>(...) throws -> T }
protocol Writable { func save<T>(...) throws }DIP с Combine
Возвращайте AnyPublisher из методов протокола, чтобы скрыть лежащий в основе тип издателя.
protocol UserStream {
func userUpdates() -> AnyPublisher<User, Never>
}DIP с async/await
Методы протокола, помеченные как async throws, полностью совместимы со структурированной конкурентностью Swift.
protocol Analytics {
func track(event: String) async
}
struct FirebaseAnalytics: Analytics {
func track(event: String) async { /* Firebase call */ }
}DIP на уровне приложения
Связывайте конкретные реализации в точке входа приложения (корне композиции), сохраняя все связи в одном месте.
@main
struct MyApp: App {
let db: Database = SQLiteDatabase()
let repo: UserRepository = RemoteUserRepository(db: db)
let useCase = FetchUserUseCase(repo: repo)
var body: some Scene {
WindowGroup { ContentView(useCase: useCase) }
}
}Тестирование с DIP
В тестах заменяйте реальные реализации имитациями или заглушками в месте внедрения.
let mockDB = InMemoryDatabase()
let service = ProfileService(db: mockDB)
// Exercise service
XCTAssertEqual(try mockDB.load(key: "profile"), expectedProfile)Быстрая проверка
Что принцип инверсии зависимостей утверждает о модулях высокого уровня?
Итоги урока
DIP: модули высокого уровня зависят от абстракций протоколов, а конкретные типы их реализуют. Выделяйте узкие протоколы, внедряйте зависимости через инициализаторы и связывайте всё в корне композиции. Это позволяет заменять реализации для тестирования, управления функциональными возможностями и будущих требований.
Часто задаваемые вопросы
Урок «Инверсия зависимостей с абстракциями протоколов» бесплатный?
Да — полный текст урока «Инверсия зависимостей с абстракциями протоколов» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Swift Academy, подпишись на CoddyKit PRO. Курс Swift Academy содержит 4 уроков всего.
Чему я научусь в уроке «Инверсия зависимостей с абстракциями протоколов»?
Зависите от абстракций, а не от конкретных реализаций, используя протоколы Swift как границы. Ты практикуешь Swift Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Swift Academy?
Предыдущий опыт не требуется. Swift Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Инверсия зависимостей с абстракциями протоколов»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Swift Academy?
Да. Каждый урок Swift Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Слои: предметная область, данные и представление
- Шаблоны сценария использования и репозитория
- Инверсия зависимостей с абстракциями протоколов
- Связывание слоёв без фреймворка DI