0Pricing
Swift Academy · Урок

Инверсия зависимостей с абстракциями протоколов

Зависите от абстракций, а не от конкретных реализаций, используя протоколы 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 — локальная установка не требуется.

Все уроки этого курса

  1. Слои: предметная область, данные и представление
  2. Шаблоны сценария использования и репозитория
  3. Инверсия зависимостей с абстракциями протоколов
  4. Связывание слоёв без фреймворка DI
← Назад к Swift Academy