Service Locator — pattern mohiyati, markaziy reyestr va DI

Muallif: IT Sectr Nashr etilgan: 2026-02-18 O'qish vaqti: 9 daq

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 olish uchun markaziy reyestr (registry)
  • DI muqobili — amalga oshirish osonroq, lekin mijozdan bog'liqliklarni yashiradi
  • Antipattern — ko'plab dasturchilar Service Locator-ni yashirin bog'liqliklar tufayli antipattern deb hisoblaydi
  • Global kirish — lokator konstruktor orqali uzatilmasdan istalgan joydan statik ravishda mavjud
  • Test — DI-dan qiyinroq: har bir test uchun global lokatorni sozlash talab qilinadi

Service Locator nima: pattern mohiyati va tuzilishi

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).

KomponentMas'uliyatSwift/Kotlin
ServiceLocatorXizmatlarga global kirishclass ServiceLocator
RegistryFabrikalar/instansiyalar ombori[ObjectIdentifier: Any]
ServiceAniq implementatsiyaNetworkService()

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: global xizmatlar reyestri

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 }.

swift
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: kechikuvchi ro'yxatdan o'tkazish

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.

kotlin
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.

Service Locator vs DI: solishtirish va qachon nima tanlash

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.

XususiyatService LocatorDependency Injection
Bog'liqliklarning ko'rinishiMetod tanasida yashirinKonstruktorda aniq
TestGlobal reyestrni sozlashKonstruktorda mock
ModullikGlobal reyestr — modulli emasTurli konteynerli modullar
MurakkablikOddiy amalga oshirish, 50-100 qatorDagger/Swinject talab qiladi
Time-to-marketTez boshlashKonteynerni 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.

Service Locator muammolari va muqobillar

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.

swift
// 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

Service Locator antipatternmi?

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.

Service Locator DI konteynerdan nima bilan farq qiladi?

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.

iOS-da Service Locator qachon ishlatilishi kerak?

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.

Service Locator-da race condition-dan qanday qochish kerak?

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.

Service Locator singletonmi?

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

  • Service Locator — statik kirish orqali xizmatlarni olish uchun markaziy reyestr
  • Yashirin bog'liqliklar — asosiy kamchilik: bog'liqliklar sinf imzosida ko'rinmaydi
  • Test — global holat va tiklash zarurati tufayli DI-dan qiyinroq
  • Swift/Kotlin — thread-safe lug'at va reified generics orqali amalga oshirish
  • Muqobil — DI (Dagger Hilt, Swinject) — ishlab chiqarish loyihalari uchun standart
  • Oqlanadi — prototiplarda, global xizmatlar va legacy loyihalarda

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.

Loyihani muhokama qilish

Shuningdek o'qing