Ответ
Service Locator (Локатор служб) — это паттерн проектирования, который предоставляет глобальный реестр для поиска и получения зависимостей (сервисов), скрывая детали их создания. Это альтернатива Dependency Injection (DI), где зависимости не "внедряются", а "запрашиваются" клиентом.
Базовая реализация на Swift:
class ServiceLocator {
static let shared = ServiceLocator()
private var services: [String: Any] = [:]
// Регистрация сервиса по его типу
func register<T>(_ service: T) {
let key = String(describing: T.self)
services[key] = service
}
// Разрешение (получение) сервиса по типу
func resolve<T>() -> T? {
let key = String(describing: T.self)
return services[key] as? T
}
}
// 1. Регистрация сервисов (обычно при запуске приложения)
ServiceLocator.shared.register(NetworkService())
ServiceLocator.shared.register(DatabaseService())
// 2. Использование в коде
class ViewModel {
// Зависимость неявно получена через локатор
private let networkService: NetworkService? = ServiceLocator.shared.resolve()
}
| Сравнение с Dependency Injection (DI): | Аспект | Service Locator | Dependency Injection (через конструктор) |
|---|---|---|---|
| Видимость зависимостей | Скрыты (класс сам их ищет) | Явные (передаются как параметры) | |
| Тестируемость | Сложнее, требует настройки локатора в тестах | Легче, зависимости можно передать как mock/stub | |
| Связность кода | Выше (класс знает о локаторе) | Ниже (класс знает только о своих интерфейсах) |
Недостатки (почему часто считается антипаттерном):
- Скрытые зависимости: Усложняет понимание того, от чего зависит класс.
- Усложняет тестирование: Требует конфигурации глобального состояния (
shared) для каждого теста. - Зависимость от глобального состояния: Противоречит принципам модульности и может привести к непредсказуемому поведению.
Ответ 18+ 🔞
Давай разберём эту штуку, про которую все такие умные говорят, а на деле — обычный глобальный ящик с зависимостями, который может и ногу отстрелить, если неаккуратно.
Service Locator (Локатор служб) — это, грубо говоря, такой глобальный шкафчик, куда ты складываешь все свои сервисы, а потом, когда надо, суёшь туда руку и пытаешься нащупать нужный. Вместо того чтобы тебе зависимости аккуратно в руки давали (как в Dependency Injection), ты сам их из этого шкафа выковыриваешь. Удобно? Иногда. Опасно? Ебать как.
Вот смотри, как это выглядит в коде, если делать по-простому:
class ServiceLocator {
static let shared = ServiceLocator() // Один на всех, глобальный как совесть у политика
private var services: [String: Any] = [:] // А тут всё лежит в одной куче, как в бардачке
// Кидаешь сервис в эту кучу
func register<T>(_ service: T) {
let key = String(describing: T.self)
services[key] = service
}
// Пытаешься найти в этой куче то, что тебе надо
func resolve<T>() -> T? {
let key = String(describing: T.self)
return services[key] as? T // А если не найдешь — получишь nil, и пиши пропало
}
}
// 1. Засовываем всё в шкаф при старте (обычно в AppDelegate, где и так бардак)
ServiceLocator.shared.register(NetworkService())
ServiceLocator.shared.register(DatabaseService())
// 2. А потом где-нибудь в коде хватаем, что попало
class ViewModel {
// А откуда это взялось? Хрен его знает, спроси у шкафа!
private let networkService: NetworkService? = ServiceLocator.shared.resolve()
}
А теперь давай сравним с нормальным Dependency Injection, чтобы понять, где собака порылась:
| Что сравниваем | Service Locator (наш шкаф) | Dependency Injection (через конструктор) |
|---|---|---|
| Видно ли зависимости? | Скрыты, как пасхалка. Класс сам шарится по глобальному шкафу. | Да чётко, как в декларации. Все зависимости перечислены при создании. |
| Как тестировать? | О, ёпта, веселье! Надо перед каждым тестом настраивать этот глобальный шкаф, а потом не забыть его почистить. | Легко и просто. Подсовываешь заглушки (mock/stub) прямо в конструктор и спишь спокойно. |
| Связанность кода | Высокая, блядь. Класс привязан к этому локатору, как алкаш к ларьку. | Низкая. Классу похуй, откуда пришли зависимости, главное — чтобы они были нужного типа. |
Почему его часто ругают и называют антипаттерном?
- Зависимости-невидимки: Открываешь класс, а нихуя не понятно, что ему нужно для работы. Всё спрятано в вызовах к локатору. Через полгода сам будешь ебаться, разбираясь.
- Тесты превращаются в ад: Представь, у тебя 100 тестов, и каждый лезет в один глобальный
sharedлокатор. Один тест его испортил — все остальные посыпались. Волнение ебать, терпения ноль ебать. - Глобальное состояние — зло: Это как ходить по охуенно тонкому льду. Одна часть приложения положила в локатор одну версию сервиса, другая — другую, и понеслась жопа. Отлаживать такое — чих-пых тебя в сраку.
Короче, инструмент как инструмент. Иногда, для мелких, глобальных и реально единичных штук (типа логгера или аналитики) — сойдёт. Но пихать в него всю бизнес-логику — это прямой путь в пиздец, чувак. Будь осторожен.