← Назад к вопросам

Что можно использовать, чтобы не передавать несколько сервисов в инициализатор?

1.2 Junior🔥 251 комментариев
#Язык Swift

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Архитектурные подходы для уменьшения количества зависимостей в инициализаторах

Проблема передачи множества сервисов в инициализатор — классический симптом "больного" конструктора (Constructor Over-Injection), который указывает на нарушение принципов Single Responsibility Principle и указывает на чрезмерную связность класса. Для решения этой проблемы в iOS разработке применяют несколько стратегий.

1. Агрегирование сервисов через контейнер или фабрику

Основной подход — создание единого объекта, который объединяет или предоставляет доступ к нескольким сервисам. Наиболее распространенные реализации:

Service Locator / DI Container:

protocol ServiceLocator {
    func resolve<T>() -> T?
}

class DefaultServiceLocator: ServiceLocator {
    private var services: [String: Any] = [:]

    func register<T>(service: T) {
        services["\(T.self)"] = service
    }

    func resolve<T>() -> T? {
        return services["\(T.self)"] as? T
    }
}

// Использование
class MyViewController {
    private let serviceLocator: ServiceLocator

    init(serviceLocator: ServiceLocator) {
        self.serviceLocator = serviceLocator
        let networkService = serviceLocator.resolve<NetworkService>()
        let databaseService = serviceLocator.resolve<DatabaseService>()
    }
}

Aggregate Service (Агрегированный сервис):

class ApplicationServices {
    let networkService: NetworkService
    let databaseService: DatabaseService
    let analyticsService: AnalyticsService
    let configService: ConfigService

    init(
        network: NetworkService,
        database: DatabaseService,
        analytics: AnalyticsService,
        config: ConfigService
    ) {
        self.networkService = network
        self.databaseService = database
        self.analyticsService = analytics
        self.configService = config
    }
}

class MyViewModel {
    private let appServices: ApplicationServices

    init(appServices: ApplicationServices) {
        self.appServices = appServices
    }
}

2. Использование паттерна Factory для создания зависимых объектов

Если класс требует множества сервисов для создания внутренних объектов, логику создания можно делегировать фабрике:

protocol MyViewModelFactory {
    func createViewModel() -> MyViewModel
}

class DefaultViewModelFactory: MyViewModelFactory {
    private let networkService: NetworkService
    private let databaseService: DatabaseService

    init(network: NetworkService, database: DatabaseService) {
        self.networkService = network
        self.databaseService = database
    }

    func createViewModel() -> MyViewModel {
        return MyViewModel(
            network: networkService,
            database: databaseService
        )
    }
}

// Теперь основной класс получает только фабрику
class MainCoordinator {
    private let viewModelFactory: MyViewModelFactory

    init(factory: MyViewModelFactory) {
        self.viewModelFactory = factory
    }

    func start() {
        let viewModel = viewModelFactory.createViewModel()
    }
}

3. Рефакторинг класса и разделение ответственности

Часто проблема множества зависимостей свидетельствует о том, что класс выполняет слишком много функций. Решение:

  • Выявление кластеров зависимостей: сгруппировать сервисы, которые используются вместе, и выделить их в отдельный компонент.
  • Применение паттерна Facade: создать упрощённый интерфейс для сложной подсистемы.
  • Разделение на несколько классов: каждый с минимальным набором зависимостей.

4. Использование функционального подхода и композиции

Для некоторых случаев, особенно в слое бизнес-логики, эффективно применение функциональных подходов:

struct MyBusinessLogic {
    let fetchData: (NetworkService) -> Result<Data, Error>
    let processData: (Data, DatabaseService) -> Void
    let trackEvent: (AnalyticsService, String) -> Void
}

// Создание композиции функций
let logic = MyBusinessLogic(
    fetchData: { network in network.fetch() },
    processData: { data, db in db.save(data) },
    trackEvent: { analytics, event in analytics.log(event) }
)

// Использование требует только сами сервисы, не хранит их
func execute(logic: MyBusinessLogic, network: NetworkService, db: DatabaseService) {
    let result = logic.fetchData(network)
    // ...
}

5. Применение Property Injection и Lazy Injection

Когда не все сервисы требуются сразу или для некоторых сервисов есть дефолтные реализации:

Property Injection (после инициализации):

class MyViewController {
    var networkService: NetworkService?
    var databaseService: DatabaseService?

    func viewDidLoad() {
        guard let network = networkService, let db = databaseService else {
            fatalError("Services not injected")
        }
        // использование
    }
}

Lazy Injection через computed property или closure:

class MyViewModel {
    private let serviceLocator: ServiceLocator

    init(serviceLocator: ServiceLocator) {
        self.serviceLocator = serviceLocator
    }

    lazy var networkService: NetworkService = {
        return serviceLocator.resolve<NetworkService>()!
    }()

    lazy var databaseService: DatabaseService = {
        return serviceLocator.resolve<DatabaseService>()!
    }()
}

Критерии выбора подхода

  1. Степень связанности: если сервисы используются совместно в многих местах — Aggregate Service или Service Locator.
  2. Сложность создания объекта: если объект требует сложной логики инстанцирования — Factory.
  3. Масштаб проекта: для крупных проектов с многими зависимостями — DI Container (например, Swinject).
  4. Тестируемость: все подходы должны сохранять возможность замены зависимостей для unit-тестов.
  5. Время жизни сервисов: если сервисы имеют разные lifecycle (синглтон vs экземпляр) — учитывать в агрегации.

Наиболее балансным решением для iOS-разработки часто становится комбинация Aggregate Service для основных сервисов и Factory для создания конкретных компонентов, что сохраняет ясность структуры и обеспечивает хорошую тестируемость.