Service Locator — xizmatlarning markaziy reyestrini (registry) taqdim etuvchi arxitektura patterni. Mijoz kodi xizmatni statik lokator orqali so'raydi, uni to'g'ridan-to'g'ri yaratmasdan yoki konstruktor orqali olmasdan. Service Locator ko'pincha Dependency Injection-ga muqobil sifatida qaraladi: amalga oshirish osonroq, lekin bog'liqliklarni yashiradi va test qilishni murakkablashtiradi. Pattern global Singleton sinfi orqali xizmatlarni ro'yxatdan o'tkazish va hal qilish bilan amalga oshiriladi. Batafsil — Martin Fowler-dan DI va Service Locator solishtiruvi.
Asosiy
Service Locator — xizmatlarni yaratish va taqdim etishni markazlashtiradigan pattern. Asosda xizmatlar reyestrini (Registry) o'z ichiga olgan Singleton sinfi (Locator) yotadi: kalit xizmatning turi (yoki identifikatori), qiymat esa aniq implementatsiya bo'lgan lug'at. Mijoz kodi ServiceLocator.resolve(ServiceProtocol.self) chaqiradi va tayyor instansiyani oladi. Pattern xizmat qanday yaratilishini belgilamaydi — fabrika, DI konteyner yoki lokator ichida new.
Pattern tuzilishi — Registry ([String: Any] tipidagi reyestr), Locator (register va resolve bilan statik sinf), Service (ro'yxatdan o'tkaziladigan xizmat). Reyestr fabrikalarni (obyekt yaratish uchun closure/lambda) yoki tayyor instansiyalarni saqlashi mumkin. Lokator global (ilova uchun bitta) yoki doiraviy (feature/modul bo'yicha) bo'lishi mumkin. Xizmatni hal qilish — lug'atda tur bo'yicha qidirish. Swift va Kotlin metatiplar orqali turni kalit sifatida ishlatadi: ObjectIdentifier(ServiceProtocol.self).
| Komponent | Mas'uliyat | Swift/Kotlin |
|---|---|---|
| ServiceLocator | Xizmatlarga global kirish | class ServiceLocator |
| Registry | Fabrikalar/instansiyalar ombori | [ObjectIdentifier: Any] |
| Service | Aniq implementatsiya | NetworkService() |
Pattern tarixi — Service Locator Java Patterns (1998) kitobida va keyinroq Core J2EE Patterns (2001) kitobida tasvirlangan. Martin Fowler 2004-yilgi maqolasida Service Locator-ni DI bilan solishtirib, Service Locator „soddaroq muqobil, ammo test uchun yomonroq“ ekanligini ta'kidlaydi. Mobil ishlanmada Service Locator Dagger va Swinject paydo bo'lishidan oldin dastlabki Android loyihalarida va iOS ilovalarida ishlatilgan. Hozir pattern ko'proq legacy loyihalarda va prototiplarda uchraydi.
Swift-da Service Locator — statik xususiyatlar va thread-safe lug'at orqali amalga oshirish. Kalit sifatida ObjectIdentifier(Protocol.self) va qiymat sifatida fabrikalar (()->Any) bilan reyestr ishlatiladi. Kechikuvchi initsializatsiya (lazy var) — standart amaliyot: xizmat birinchi so'rovda yaratiladi. Swift resolve vaqtida turni aniq konvertatsiya qilishni talab qiladi: guard let service = locator.resolve(ServiceProtocol.self) else { return }.
final class ServiceLocator {
static let shared = ServiceLocator()
private var registry: [ObjectIdentifier: Any] = [:]
private let lock = NSLock()
func register<T>(_ type: T.Type, factory: @escaping () -> T) {
lock.lock()
registry[ObjectIdentifier(type)] = factory
lock.unlock()
}
func resolve<T>(_ type: T.Type) -> T {
lock.lock()
defer { lock.unlock() }
guard let factory = registry[ObjectIdentifier(type)] as? () -> T else {
fatalError("Service \(type) not registered")
}
return factory()
}
func reset() {
lock.lock()
registry.removeAll()
lock.unlock()
}
}
// Ro'yxatdan o'tkazish
ServiceLocator.shared.register(NetworkServiceProtocol.self) { NetworkService() }
// Foydalanish
let network = ServiceLocator.shared.resolve(NetworkServiceProtocol.self)
Doirani boshqarish — lokator fabrikalarni (transient — har safar yangi obyekt) yoki tayyor instansiyalarni (singleton) saqlashi mumkin. Fabrikalar uchun har bir resolve chaqiruvida bajariladigan closure ro'yxatdan o'tkaziladi. Singleton uchun — yaratilgan instansiyani tutib qoladigan closure. Doira qo'shish: .transient, .singleton, .weak (zaif havola — obyekt kimdir havolani ushlab turguncha yashaydi). Weak doirasi UIKit ViewController uchun qulay bo'lib, pop/dismiss vaqtida xotira oqishini oldini oladi.
Kotlin-da Service Locator — tip xavfsizligi uchun reified inline funksiyalari bilan object (Singleton) orqali ixcham amalga oshirish. Kotlin qisqa lokator yaratishga imkon beradi: val service by locator<ServiceProtocol>() delegat bilan, bu kodni tozaroq qiladi. Reified generics (<reified T>) ObjectIdentifier o'rnini bosadi — tip generikdan olinadi. Kotlin lokatorlari ko'pincha aniq blokirovkalarsiz thread-safe bo'lish uchun ConcurrentHashMap ishlatadi.
object ServiceLocator {
private val registry = ConcurrentHashMap<Class<*>, () -> Any>()
inline fun <reified T: Any> register(noinline factory: () -> T) {
registry[T::class.java] = factory
}
@Suppress("UNCHECKED_CAST")
inline fun <reified T: Any> resolve(): T {
val factory = registry[T::class.java]
?: throw IllegalStateException("Service ${T::class.simpleName} not registered")
return factory() as T
}
fun clear() {
registry.clear()
}
}
// Ro'yxatdan o'tkazish
ServiceLocator.register<ApiService> { RetrofitApiService() }
// Sinfda foydalanish
class UserRepository {
private val api: ApiService = ServiceLocator.resolve()
private val db: Database = ServiceLocator.resolve()
}
// Kechikuvchi hal qilish uchun delegat
class LocatorDelegate<reified T: Any> : Lazy<T> {
override val value: T get() = ServiceLocator.<T>resolve()
override fun isInitialized(): Boolean = true
}
inline fun <reified T: Any> locator(): Lazy<T> = LocatorDelegate()
// Foydalanish: val api by locator()
Android-da Service Locator — Dagger paydo bo'lishidan oldin eski loyihalarda uchraydi. Jetpack Hilt va Koin Android hamjamiyatida Service Locator-ni siqib chiqardi. Biroq, lokator modul testlari uchun dolzarb bo'lib qolmoqda: Hilt-siz mock-xizmatlar bilan oddiy ServiceLocator taqlidi. Plyus: testlar uchun Dagger kompilyatsiyasini kutish shart emas. Minus: agar testda lokatorni almashtirishni unutsangiz, testlar ishlab chiqarish xizmatlaridan foydalanadi.
Bog'liqliklarning aniqliligi — asosiy farq. DI bog'liqliklarni aniq e'lon qiladi: init(service: ServiceProtocol) — har qanday IDE sinfning bog'liqliklarini ko'rsatadi. Service Locator ularni yashiradi: dependencies = ServiceLocator.resolve() — metod ichida yashirin. DI bilan sinfning barcha bog'liqliklarini darhol ko'rish mumkin, Service Locator bilan sinfning butun tanasini o'qish kerak. Bu Service Locator kodini kamroq bashorat qilinadigan qiladi: reyestrda o'zgarish lokatordan foydalanadigan istalgan sinfni buzishi mumkin.
| Xususiyat | Service Locator | Dependency Injection |
|---|---|---|
| Bog'liqliklarning ko'rinishi | Metod tanasida yashirin | Konstruktorda aniq |
| Test | Global reyestrni sozlash | Konstruktorda mock |
| Modullik | Global reyestr — modulli emas | Turli konteynerli modullar |
| Murakkablik | Oddiy amalga oshirish, 50-100 qator | Dagger/Swinject talab qiladi |
| Time-to-market | Tez boshlash | Konteynerni sozlash |
Service Locator qachon oqlanadi — prototiplar va MVP (sozlamasiz tez boshlash). DI ramkasini qo'shib bo'lmaydigan legacy loyihalar (murakkab kompilyatsiya, linter cheklovlari). Asbobiy kutubxonalar (loglash, crash-reporting) — ular baribir global. 3 dasturchidan iborat jamoasi bo'lgan ishlab chiqarish ilovalari uchun DI tavsiya etiladi: aniq bog'liqliklar refaktoringdagi xatolar sonini kamaytiradi va yangi dasturchilarning loyihaga kirishini osonlashtiradi.
Yashirin bog'liqliklar — metod ichida ServiceLocator.resolve() ishlatadigan sinfni statik tahlil qilib bo'lmaydi. IDE bog'liqliklarni ko'rsatmaydi, kompilyator xizmat ro'yxatdan o'tganligini tekshirmaydi. „Service not registered“ xatosi faqat ish vaqtida yuz beradi. Refaktoring xavfli bo'ladi: reyestrdan xizmatni olib tashlash ilovadagi istalgan sinfni buzishi mumkin. DI bu muammoni kompilyatsiya vaqti tekshiruvlari (Dagger) yoki aniq konstruktorlar orqali hal qiladi.
Test muammosi — har bir test barcha bog'liqliklar bilan ServiceLocator.shared-ni sozlashi kerak. Testdan so'ng — holatni tiklash (reset()). Testlarning parallel bajarilishida ServiceLocator.shared-ning global holati race condition-ga olib keladi: bir test mock ro'yxatdan o'tkazadi, boshqasi — boshqaning mock-ni oladi. Yechim: doiraviy lokatorlar (test boshiga bitta) yoki ThreadLocal. DI bu muammoni tubdan hal qiladi: har bir test o'z instansiyasini mock-bog'liqliklar bilan yaratadi.
// Service Locator test muammosi
class LoginViewModelTests: XCTestCase {
override func setUp() {
super.setUp()
// Test uchun global reyestrni sozlash
ServiceLocator.shared.register(AuthServiceProtocol.self) { MockAuthService() }
}
override func tearDown() {
ServiceLocator.shared.reset()
super.tearDown()
}
func testLogin() {
let viewModel = LoginViewModel() // ichida ServiceLocator ishlatadi
// test...
}
}
Service Locator muqobillari — DI (Dagger, Swinject), Factory Method, Ambient Context. Factory Method — global holatsiz oddiy pattern: fabrika sinfi xizmatlarni yaratadi va konstruktor orqali uzatiladi. Ambient Context — cross-cutting concerns (loglash, autentifikatsiya) uchun thread-safe muqobil. Eng yaxshi muqobil — DI ramkasisiz qo'lda Constructor Injection: fabrikada bog'liqliklarni aniq yaratish va konstruktor orqali uzatish Dagger/Swinject sozlash murakkabligisiz DI ning ravshanligini beradi.
Tez-tez beriladigan savollar
Ko'plab dasturchilar Service Locator-ni antipattern deb hisoblaydi, chunki u bog'liqliklarni yashiradi, testni qiyinlashtiradi va sinflar orasida yashirin aloqalar yaratadi. Biroq, prototiplarda, kichik loyihalarda va global xizmatlar (loglash, analitika) uchun Service Locator oqlanishi mumkin. Qaror kontekstga bog'liq: jamoasi bor ishlab chiqarish ilovasi uchun — DI, bitta dasturchining prototipi uchun — Service Locator.
DI konteyneri (Dagger, Swinject) bog'liqliklarni obyektga avtomatik joylashtiradi — obyekt konteyner mavjudligini bilmaydi. Service Locator — obyekt o'zi bog'liqliklarni reyestrdan so'raydi. DI konteyneri IoC prinsipiga amal qiladi, Service Locator — buzadi: obyekt o'z bog'liqliklarini olishni boshqaradi. DI konteyneri obyekt yaratilishidan oldin (konstruktor orqali) ishlaydi, Service Locator — kodning istalgan joyida.
Service Locator iOS-da quyidagi hollarda oqlanadi: global xizmatlar (Analytics, Logger, Crashlytics), Swinject sozlash ortiqcha bo'lgan prototiplar va katta legacy modullarining modul testlari uchun. Yangi iOS loyihalari uchun Swinject yoki konstruktor orqali qo'lda DI tavsiya etiladi. Environment bilan SwiftUI — shuningdek, Service Locator-dan qochadigan DI shakli.
Thread-safe kolleksiyadan foydalaning (Swift-da NSLock, Kotlin-da ConcurrentHashMap). Testlar uchun — ThreadLocal yoki doiraviy lokator. Muqobil: async-local storage — xizmatlar korutina/aktorga bog'langan. Eng yaxshi yechim — parallel testlar uchun Service Locator-dan qochish va har bir test uchun aniq obyekt yaratish bilan DI dan foydalanish.
Odatda Service Locator Singleton sifatida amalga oshiriladi, lekin bu majburiy emas. Modul uchun lokator instansiyasini (feature-scoped locator) yaratib, uni konstruktor orqali uzatish mumkin. Feature-scoped lokator global holat muammosini hal qiladi, lekin yashirin bog'liqliklar muammosini hal qilmaydi. Bunday pattern Ambient Context yoki Scoped Locator deb ataladi.
Xulosa
Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz
IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.