Service Locator — modelin özü, merkezi kayıt ve DI

Yazar: IT Sectr Yayınlanma: 2026-02-18 Okuma süresi: 9 dk

Service Locator, merkezi bir hizmet kaydı sağlayan mimari bir desendir. İstemci kodu, doğrudan oluşturmadan veya kurucu aracılığıyla almadan, statik bir konumlandırıcı aracılığıyla bir hizmet talep eder. Service Locator genellikle Dependency Injection'a bir alternatif olarak kabul edilir: uygulaması daha basittir, ancak bağımlılıkları gizler ve test etmeyi karmaşıklaştırır. Desen, hizmet kaydı ve çözümlemesi olan küresel bir Singleton sınıfı aracılığıyla uygulanır. Daha fazla detay — Martin Fowler'ın DI ve Service Locator karşılaştırmasında.

Önemli Noktalar

  • Service Locator — hizmetleri almak için merkezi bir kayıt
  • DI alternatifi — uygulaması daha basit, ancak bağımlılıkları istemciden gizler
  • Anti-desen — birçok geliştirici, gizli bağımlılıklar nedeniyle Service Locator'ı anti-desen olarak kabul eder
  • Küresel erişim — konumlandırıcı, kurucu aracılığıyla iletilmeden, her yerden statik olarak erişilebilir
  • Test — DI'dan daha karmaşık: her test için küresel konumlandırıcının yapılandırılması gerekir

Service Locator Nedir: modelin özü ve yapısı

Service Locator, hizmetlerin oluşturulmasını ve sağlanmasını merkezileştiren bir desendir. Hizmetlerin bir kaydını içeren bir Singleton sınıfına (Locator) dayanır: anahtarın hizmet türü (veya tanımlayıcı) ve değerin somut uygulama olduğu bir sözlük. İstemci kodu ServiceLocator.resolve(ServiceProtocol.self) çağrısını yapar ve hazır bir örnek alır. Desen, hizmetin nasıl oluşturulacağını belirlemez — fabrika, DI kabı veya konumlandırıcı içinde new.

Desen yapısı — Kayıt ([String: Any] türünde sözlük), Konumlandırıcı (register ve resolve ile statik sınıf), Hizmet (kaydedilen hizmet). Kayıt, fabrikaları (nesne oluşturmak için closure/lambda) veya hazır örnekleri depolayabilir. Konumlandırıcı küresel (uygulama başına bir tane) veya kapsamlı (özellik/modül başına) olabilir. Hizmet çözümlemesi, türe göre sözlükte aramadır. Swift ve Kotlin, türü meta türler aracılığıyla anahtar olarak kullanır: ObjectIdentifier(ServiceProtocol.self).

BileşenSorumlulukSwift/Kotlin
ServiceLocatorHizmetlere küresel erişimclass ServiceLocator
RegistryFabrika/örnek deposu[ObjectIdentifier: Any]
ServiceSomut uygulamaNetworkService()

Desenin tarihi — Service Locator, Java Patterns (1998) kitabında ve daha sonra Core J2EE Patterns (2001)'de tanımlanmıştır. Martin Fowler'ın 2004 makalesi Service Locator'ı DI ile karşılaştırır ve Service Locator'ın "daha basit bir alternatif, ancak test için daha kötü" olduğunu belirtir. Mobil geliştirmede, Service Locator, Dagger ve Swinject ortaya çıkmadan önce erken Android projelerinde ve iOS uygulamalarında kullanıldı. Şimdi desen daha çok eski projelerde ve prototiplerde bulunur.

Swift'te Service Locator: küresel hizmet kaydı

Swift'te Service Locator — statik özellikler ve iş parçacığı güvenli sözlük aracılığıyla uygulama. ObjectIdentifier(Protocol.self) anahtar ve fabrikalar (() -> Any) değer olarak kullanılan bir kayıt kullanılır. Tembel başlatma (lazy var) standart uygulamadır: hizmet ilk talep üzerine oluşturulur. Swift, resolve sırasında açık tür dönüşümü gerektirir: 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()
    }
}

// Kayıt
ServiceLocator.shared.register(NetworkServiceProtocol.self) { NetworkService() }

// Kullanım
let network = ServiceLocator.shared.resolve(NetworkServiceProtocol.self)

Kapsam yönetimi — konumlandırıcı fabrikaları (transient — her seferinde yeni nesne) veya hazır örnekleri (singleton) depolayabilir. Fabrikalar için, her resolve'da çağrılan bir closure kaydedilir. Singleton için, oluşturulan örneği yakalayan bir closure kullanılır. Kapsam ekleme: .transient, .singleton, .weak (zayıf referans — nesne, birisi referans tuttuğu sürece yaşar). UIKit UIViewController'lar için pop/dismiss sırasında sızıntıları önlemek için Weak kapsamı kullanışlıdır.

Kotlin'de Service Locator: tembel kayıt

Kotlin'de Service Locator — tür güvenliği için inline reified işlevlerle object (Singleton) aracılığıyla kompakt bir uygulama. Kotlin, bir temsilci ile kısa bir konumlandırıcıya izin verir: val service by locator(), kodu daha temiz hale getirir. Reified jenerikler () ObjectIdentifier yerine geçer — tür jenerikten elde edilir. Kotlin konumlandırıcıları genellikle açık kilitler olmadan iş parçacığı güvenliği için ConcurrentHashMap kullanır.

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()
    }
}

// Kayıt
ServiceLocator.register<ApiService> { RetrofitApiService() }

// Sınıfta kullanım
class UserRepository {
    private val api: ApiService = ServiceLocator.resolve()
    private val db: Database = ServiceLocator.resolve()
}

// Tembel çözümleme için temsilci
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()
// Kullanım: val api by locator()

Android'de Service Locator — Dagger öncesi eski projelerde bulunur. Jetpack Hilt ve Koin, Android topluluğunda Service Locator'ın yerini almıştır. Ancak, konumlandırıcı birim testleri için geçerliliğini korur: Hilt olmadan mock hizmetlerle basit bir ServiceLocator saplaması. Artı: testler için Dagger derlemesini beklemeye gerek yok. Eksi: testte konumlandırıcıyı geçersiz kılmayı unutursanız, testler üretim hizmetlerini kullanır.

Service Locator vs DI: karşılaştırma ve ne zaman ne seçilmeli

Bağımlılıkların açıklığı — temel fark. DI bağımlılıkları açıkça bildirir: init(service: ServiceProtocol) — herhangi bir IDE sınıf bağımlılıklarını gösterir. Service Locator onları gizler: dependencies = ServiceLocator.resolve() — yöntemin içinde gizli. DI ile tüm sınıf bağımlılıklarını hemen görebilirsiniz; Service Locator ile tüm sınıf gövdesini okumanız gerekir. Bu, Service Locator kodunu daha az tahmin edilebilir yapar: kaydı değiştirmek, konumlandırıcıyı kullanan herhangi bir sınıfı bozabilir.

ÖzellikService LocatorDependency Injection
Bağımlılık görünürlüğüYöntem gövdelerinde gizliKurucuda açık
TestKüresel kaydı yapılandırKurucuda mock
ModülerlikKüresel kayıt — modüler değilAyrı kaplarla modüller
KarmaşıklıkBasit uygulama, 50-100 satırDagger/Swinject gerekli
Pazara çıkış süresiHızlı başlangıçKapsayıcı kurulumu gerekli

Service Locator ne zaman haklı — prototipler ve MVP'ler (yapılandırma olmadan hızlı başlangıç). DI çerçevesi eklenemeyen eski projeler (karmaşık derleme, lint kısıtlamaları). Enstrümantasyon kitaplıkları (günlükleme, çökme raporlama) — zaten küreseldir. 3+ geliştiricili ekip ile üretim uygulamaları için DI önerilir: açık bağımlılıklar yeniden düzenleme sırasındaki hata sayısını azaltır ve yeni geliştiricilerin işe alınmasını basitleştirir.

Service Locator sorunları ve alternatifleri

Gizli bağımlılıklar — bir yöntem içinde ServiceLocator.resolve() kullanan bir sınıf statik olarak analiz edilemez. IDE bağımlılıkları göstermez, derleyici hizmetin kayıtlı olup olmadığını kontrol etmez. "Service not registered" hatası yalnızca çalışma zamanında oluşur. Yeniden düzenleme tehlikeli hale gelir: kayıttan bir hizmeti kaldırmak, uygulamadaki herhangi bir sınıfı bozabilir. DI bu sorunu derleme zamanı kontrolleri (Dagger) veya açık kurucular aracılığıyla çözer.

Test sorunu — her test tüm bağımlılıklarla ServiceLocator.shared yapılandırmalıdır. Testten sonra — durumu sıfırlayın. Paralel test yürütme sırasında, ServiceLocator.shared küresel durumu yarış koşullarına yol açar: bir test bir mock kaydeder, diğer test başkasının mock'unu alır. Çözüm: kapsamlı konumlandırıcılar (test başına bir tane) veya ThreadLocal. DI bu sorunu en baştan çözer: her test mock bağımlılıklarla kendi örneğini oluşturur.

swift
// Service Locator test sorunu
class LoginViewModelTests: XCTestCase {
    override func setUp() {
        super.setUp()
        // Test için küresel kayıt kurulumu
        ServiceLocator.shared.register(AuthServiceProtocol.self) { MockAuthService() }
    }

    override func tearDown() {
        ServiceLocator.shared.reset()
        super.tearDown()
    }

    func testLogin() {
        let viewModel = LoginViewModel() // dahili olarak ServiceLocator kullanır
        // test...
    }
}

Service Locator alternatifleri — DI (Dagger, Swinject), Factory Method, Ambient Context. Factory Method — küresel durum olmadan basit bir desen: bir fabrika sınıfı hizmetler oluşturur ve kurucu aracılığıyla iletilir. Ambient Context — çapraz kesen endişeler (günlükleme, yetkilendirme) için iş parçacığı güvenli bir alternatif. En iyi alternatif, DI çerçevesi olmadan manuel fabrika ile Constructor Injection'dır: kurucu iletimi ile bir fabrikada bağımlılıkların açıkça oluşturulması, Dagger/Swinject kurulumunun karmaşıklığı olmadan DI netliği sağlar.

Sıkça Sorulan Sorular

Service Locator bir anti-desen midir?

Birçok geliştirici, Service Locator'ı bağımlılıkları gizlediği, test etmeyi karmaşıklaştırdığı ve sınıflar arasında gizli bağlantı oluşturduğu için anti-desen olarak kabul eder. Ancak, prototiplerde, küçük projelerde ve küresel hizmetler (günlükleme, analitik) için Service Locator haklı olabilir. Karar bağlama bağlıdır: ekipli bir üretim uygulaması için — DI, prototipteki tek bir geliştirici için — Service Locator.

Service Locator, DI kapsayıcısından nasıl farklıdır?

Bir DI kapsayıcısı (Dagger, Swinject) bağımlılıkları otomatik olarak bir nesneye enjekte eder — nesne kapsayıcının varlığından haberdar değildir. Service Locator — nesne bağımlılıkları kayıttan kendisi talep eder. Bir DI kapsayıcısı IoC ilkesini takip eder; Service Locator onu ihlal eder: nesne kendi bağımlılıklarının elde edilmesini yönetir. Bir DI kapsayıcısı nesne oluşturmadan önce (kurucu aracılığıyla) çalışır; Service Locator — kodun herhangi bir yerinde.

iOS'te Service Locator ne zaman kullanılır?

Service Locator iOS'te şunlar için haklıdır: küresel hizmetler (Analytics, Logger, Crashlytics), Swinject kurulumunun aşırı olduğu prototipler ve büyük eski modüllerin birim testleri. Yeni iOS projeleri için Swinject veya kurucu aracılığıyla manuel DI önerilir. @Environment ile SwiftUI — ayrıca Service Locator'dan kaçınan bir DI biçimidir.

Service Locator'da yarış koşulları nasıl önlenir?

İş parçacığı güvenli bir koleksiyon kullanın (Swift'te NSLock, Kotlin'de ConcurrentHashMap). Testler için — ThreadLocal veya kapsamlı konumlandırıcı. Alternatif: async-local depolama — hizmetler bir coroutine/actor'e bağlı. En iyi çözüm, paralel testler için Service Locator'dan kaçınmak ve her test için açık nesne oluşturma ile DI kullanmaktır.

Service Locator bir singleton mıdır?

Service Locator tipik olarak Singleton olarak uygulanır, ancak bu zorunlu değildir. Bir modül için bir konumlandırıcı örneği (özellik kapsamlı konumlandırıcı) oluşturabilir ve bunu kurucu aracılığıyla iletebilirsiniz. Özellik kapsamlı konumlandırıcı küresel durum sorununu çözer ancak gizli bağımlılıklar sorununu çözmez. Bu desen Ambient Context veya Scoped Locator olarak adlandırılır.

Özet

  • Service Locator — statik erişim yoluyla hizmet almak için merkezi bir kayıt
  • Gizli bağımlılıklar — ana dezavantaj: bağımlılıklar sınıf imzasında görünmez
  • Test — küresel durum ve sıfırlama ihtiyacı nedeniyle DI'dan daha karmaşık
  • Swift/Kotlin — iş parçacığı güvenli sözlük ve reified jenerikler aracılığıyla uygulama
  • Alternatif — DI (Dagger Hilt, Swinject) — üretim projeleri için standart
  • Haklı — prototiplerde, küresel hizmetler ve eski projelerde

Anahtar teslim bir mobil uygulama geliştireceğiz

IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.

Projeyi tartış

Ayrıca okuyun