Service Locator — پیٹرن کا جوہر، مرکزی رجسٹری اور DI

مصنف: IT Sectr اشاعت: 2026-02-18 مطالعے کا وقت: 9 منٹ

Service Locator ایک آرکیٹیکچرل پیٹرن ہے جو خدمات کی ایک مرکزی رجسٹری فراہم کرتا ہے۔ کلائنٹ کوڈ ایک جامد لوکیٹر کے ذریعے سروس کی درخواست کرتا ہے، بغیر اسے براہ راست بنائے یا کنسٹرکٹر کے ذریعے حاصل کئے۔ Service Locator کو اکثر Dependency Injection کے متبادل کے طور پر دیکھا جاتا ہے: یہ نافذ کرنے میں آسان ہے، لیکن انحصار کو چھپاتا ہے اور جانچ کو پیچیدہ بناتا ہے۔ پیٹرن کو سروس رجسٹریشن اور ریزولوشن کے ساتھ ایک عالمی Singleton کلاس کے ذریعے نافذ کیا جاتا ہے۔ مزید تفصیلات — Martin Fowler کے DI اور Service Locator کے موازنہ میں۔

اہم نکات

  • Service Locator — خدمات حاصل کرنے کے لئے ایک مرکزی رجسٹری
  • DI کا متبادل — نافذ کرنے میں آسان، لیکن کلائنٹ سے انحصار چھپاتا ہے
  • اینٹی پیٹرن — بہت سے ڈویلپرز چھپے ہوئے انحصار کی وجہ سے Service Locator کو اینٹی پیٹرن سمجھتے ہیں
  • عالمی رسائی — لوکیٹر کنسٹرکٹر کے ذریعے منتقل کئے بغیر، کہیں سے بھی جامد طور پر قابل رسائی ہے
  • جانچ — DI سے زیادہ پیچیدہ: ہر ٹیسٹ کے لئے عالمی لوکیٹر کی ترتیب درکار ہے

Service Locator کیا ہے: پیٹرن کا جوہر اور ساخت

Service Locator ایک پیٹرن ہے جو خدمات کی تخلیق اور فراہمی کو مرکزی بناتا ہے۔ یہ ایک Singleton کلاس (Locator) پر مبنی ہے جس میں خدمات کی ایک رجسٹری ہوتی ہے: ایک لغت جہاں کلید سروس کی قسم (یا شناخت کنندہ) ہے اور قدر ٹھوس نفاذ ہے۔ کلائنٹ کوڈ ServiceLocator.resolve(ServiceProtocol.self) کو کال کرتا ہے اور ایک تیار مثال حاصل کرتا ہے۔ پیٹرن یہ طے نہیں کرتا کہ سروس کیسے بنائی جاتی ہے — فیکٹری، DI کنٹینر یا لوکیٹر کے اندر new۔

پیٹرن کی ساخت — رجسٹری ([String: Any] قسم کی لغت)، لوکیٹر (register اور resolve کے ساتھ جامد کلاس)، سروس (رجسٹر کی جا رہی سروس)۔ رجسٹری فیکٹریوں (آبجیکٹ بنانے کے لئے closures/lambdas) یا تیار مثالوں کو ذخیرہ کر سکتی ہے۔ لوکیٹر عالمی (ایک فی ایپلیکیشن) یا دائرہ کار والا (فی فیچر/ماڈیول) ہو سکتا ہے۔ سروس کا حل قسم کے لحاظ سے لغت میں تلاش ہے۔ Swift اور Kotlin میٹا ٹائپس کے ذریعے قسم کو کلید کے طور پر استعمال کرتے ہیں: ObjectIdentifier(ServiceProtocol.self)۔

جزوذمہ داریSwift/Kotlin
ServiceLocatorخدمات تک عالمی رسائیclass ServiceLocator
Registryفیکٹریوں/مثالوں کا ذخیرہ[ObjectIdentifier: Any]
Serviceٹھوس نفاذNetworkService()

پیٹرن کی تاریخ — Service Locator کو Java Patterns (1998) کتاب میں اور بعد میں Core J2EE Patterns (2001) میں بیان کیا گیا۔ مارٹن فاؤلر کے 2004 کے مضمون میں Service Locator کا DI سے موازنہ کیا گیا ہے، جس میں بتایا گیا ہے کہ Service Locator "ایک آسان متبادل ہے، لیکن جانچ کے لئے برا ہے۔" موبائل ڈویلپمنٹ میں، Dagger اور Swinject کے آنے سے پہلے ابتدائی Android منصوبوں اور iOS ایپس میں Service Locator استعمال ہوتا تھا۔ اب پیٹرن زیادہ تر وراثتی منصوبوں اور پروٹوٹائپس میں پایا جاتا ہے۔

Swift میں Service Locator: عالمی سروس رجسٹری

Swift میں Service Locator — جامد خصوصیات اور تھریڈ سیف لغت کے ذریعے نفاذ۔ ObjectIdentifier(Protocol.self) کو کلید اور فیکٹریوں (() -> Any) کو قدروں کے طور پر استعمال کرنے والی رجسٹری استعمال ہوتی ہے۔ سست ابتدا (lazy var) معیاری عمل ہے: سروس پہلی درخواست پر بنائی جاتی ہے۔ Swift کو resolve کے دوران واضح قسم کی تبدیلی کی ضرورت ہوتی ہے: 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()
    }
}

// رجسٹریشن
ServiceLocator.shared.register(NetworkServiceProtocol.self) { NetworkService() }

// استعمال
let network = ServiceLocator.shared.resolve(NetworkServiceProtocol.self)

دائرہ کار کا انتظام — لوکیٹر فیکٹریوں (transient — ہر بار نیا آبجیکٹ) یا تیار مثالوں (singleton) کو ذخیرہ کر سکتا ہے۔ فیکٹریوں کے لئے، ایک closure رجسٹر کیا جاتا ہے جو ہر resolve پر کال ہوتا ہے۔ Singleton کے لئے، ایک closure بنائی گئی مثال کو کیپچر کرتا ہے۔ دائرہ کار شامل کرنا: .transient، .singleton، .weak (کمزور حوالہ — آبجیکٹ اس وقت تک زندہ رہتا ہے جب تک کوئی حوالہ رکھتا ہے)۔ UIKit UIViewControllers کے لئے pop/dismiss پر رساؤ سے بچنے کے لئے Weak دائرہ کار آسان ہے۔

Kotlin میں Service Locator: سست رجسٹریشن

Kotlin میں Service Locator — قسم کی حفاظت کے لئے inline reified افعال کے ساتھ object (Singleton) کے ذریعے ایک کمپیکٹ نفاذ۔ Kotlin ایک مندوب کے ساتھ ایک مختصر لوکیٹر کی اجازت دیتا ہے: val service by locator()، کوڈ کو صاف ستھرا بناتا ہے۔ Reified generics () ObjectIdentifier کی جگہ لیتے ہیں — قسم generic سے حاصل ہوتی ہے۔ Kotlin لوکیٹر اکثر واضح تالے کے بغیر تھریڈ سیفٹی کے لئے ConcurrentHashMap استعمال کرتے ہیں۔

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

// رجسٹریشن
ServiceLocator.register<ApiService> { RetrofitApiService() }

// کلاس میں استعمال
class UserRepository {
    private val api: ApiService = ServiceLocator.resolve()
    private val db: Database = ServiceLocator.resolve()
}

// سست حل کے لئے مندوب
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()
// استعمال: val api by locator()

Android میں Service Locator — Dagger سے پہلے پرانے منصوبوں میں پایا جاتا ہے۔ Jetpack Hilt اور Koin نے Android کمیونٹی میں Service Locator کی جگہ لے لی ہے۔ تاہم، لوکیٹر یونٹ ٹیسٹ کے لئے متعلقہ رہتا ہے: Hilt کے بغیر mock خدمات کے ساتھ ایک سادہ ServiceLocator سٹب۔ فائدہ: ٹیسٹوں کے لئے Dagger کی compilation کا انتظار کرنے کی ضرورت نہیں۔ نقصان: اگر آپ ٹیسٹ میں لوکیٹر کو اوور رائڈ کرنا بھول جاتے ہیں، تو ٹیسٹ پروڈکشن خدمات استعمال کرتے ہیں۔

Service Locator vs DI: موازنہ اور کب کیا انتخاب کریں

انحصار کی واضحیت — بنیادی فرق۔ DI انحصار کو واضح طور پر اعلان کرتا ہے: init(service: ServiceProtocol) — کوئی بھی IDE کلاس کے انحصار دکھاتا ہے۔ Service Locator انہیں چھپاتا ہے: dependencies = ServiceLocator.resolve() — طریقہ کار کے اندر چھپا ہوا۔ DI کے ساتھ، آپ فوری طور پر تمام کلاس انحصار دیکھ سکتے ہیں؛ Service Locator کے ساتھ، آپ کو پورا کلاس باڈی پڑھنے کی ضرورت ہے۔ یہ Service Locator کوڈ کو کم پیش قیاسی بناتا ہے: رجسٹری کو تبدیل کرنا لوکیٹر استعمال کرنے والی کسی بھی کلاس کو توڑ سکتا ہے۔

خصوصیتService LocatorDependency Injection
انحصار کی نمائشطریقہ کار کے اندر چھپےکنسٹرکٹر میں واضح
جانچعالمی رجسٹری ترتیب دیںکنسٹرکٹر میں mock
ماڈیولریٹیعالمی رجسٹری — ماڈیولر نہیںالگ کنٹینر والے ماڈیول
پیچیدگیسادہ نفاذ، 50-100 سطریںDagger/Swinject درکار
مارکیٹ میں آنے کا وقتتیز شروعکنٹینر سیٹ اپ درکار

Service Locator کب جائز ہے — پروٹوٹائپ اور MVP (ترتیب کے بغیر تیز شروع)۔ وراثتی منصوبے جہاں DI فریم ورک شامل کرنا ناممکن ہے (پیچیدہ تعمیر، لنٹر پابندیاں)۔ آلات کی لائبریریاں (لاگنگ، کریش رپورٹنگ) — وہ پہلے سے ہی عالمی ہیں۔ 3+ ڈویلپرز کی ٹیم کے ساتھ پروڈکشن ایپلیکیشنز کے لئے، DI تجویز کیا جاتا ہے: واضح انحصار ری فیکٹرنگ کے دوران غلطیوں کی تعداد کو کم کرتے ہیں اور نئے ڈویلپرز کو شامل کرنا آسان بناتے ہیں۔

Service Locator کے مسائل اور متبادل

چھپے ہوئے انحصار — ایک کلاس جو طریقہ کار کے اندر ServiceLocator.resolve() استعمال کرتی ہے، جامد طور پر تجزیہ نہیں کی جا سکتی۔ IDE انحصار نہیں دکھاتا، کمپائلر چیک نہیں کرتا کہ سروس رجسٹر ہے یا نہیں۔ "Service not registered" خرابی صرف رن ٹائم پر ہوتی ہے۔ ری فیکٹرنگ خطرناک ہو جاتی ہے: رجسٹری سے سروس ہٹانا ایپلیکیشن میں کسی بھی کلاس کو توڑ سکتا ہے۔ DI اس مسئلے کو کمپائل ٹائم چیک (Dagger) یا واضح کنسٹرکٹرز کے ذریعے حل کرتا ہے۔

جانچ کا مسئلہ — ہر ٹیسٹ کو تمام انحصار کے ساتھ ServiceLocator.shared ترتیب دینا چاہیے۔ ٹیسٹ کے بعد — حالت دوبارہ سیٹ کریں۔ متوازی ٹیسٹ پر عملدرآمد کے دوران، ServiceLocator.shared کی عالمی حالت دوڑ کے حالات کا باعث بنتی ہے: ایک ٹیسٹ mock رجسٹر کرتا ہے، دوسرا ٹیسٹ کسی اور کا mock حاصل کرتا ہے۔ حل: دائرہ کار والے لوکیٹر (ایک فی ٹیسٹ) یا ThreadLocal۔ DI اس مسئلے کو شروع سے حل کرتا ہے: ہر ٹیسٹ mock انحصار کے ساتھ اپنی مثال بناتا ہے۔

swift
// Service Locator کی جانچ کا مسئلہ
class LoginViewModelTests: XCTestCase {
    override func setUp() {
        super.setUp()
        // ٹیسٹ کے لئے عالمی رجسٹری ترتیب
        ServiceLocator.shared.register(AuthServiceProtocol.self) { MockAuthService() }
    }

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

    func testLogin() {
        let viewModel = LoginViewModel() // اندرونی طور پر ServiceLocator استعمال کرتا ہے
        // ٹیسٹ...
    }
}

Service Locator کے متبادل — DI (Dagger, Swinject)، Factory Method، Ambient Context۔ Factory Method — عالمی حالت کے بغیر ایک سادہ پیٹرن: ایک فیکٹری کلاس خدمات بناتی ہے اور کنسٹرکٹر کے ذریعے منتقل کی جاتی ہے۔ Ambient Context — کراس کٹنگ خدشات (لاگنگ، اجازت) کے لئے ایک تھریڈ سیف متبادل۔ بہترین متبادل DI فریم ورک کے بغیر دستی فیکٹری کے ساتھ Constructor Injection ہے: کنسٹرکٹر منتقلی کے ساتھ فیکٹری میں انحصار کی واضح تخلیق Dagger/Swinject کی ترتیب کی پیچیدگی کے بغیر DI وضاحت فراہم کرتی ہے۔

اکثر پوچھے گئے سوالات

کیا Service Locator ایک اینٹی پیٹرن ہے؟

بہت سے ڈویلپرز Service Locator کو اینٹی پیٹرن سمجھتے ہیں کیونکہ یہ انحصار چھپاتا ہے، جانچ کو پیچیدہ بناتا ہے اور کلاسوں کے درمیان چھپا تعلق پیدا کرتا ہے۔ تاہم، پروٹوٹائپس، چھوٹے منصوبوں اور عالمی خدمات (لاگنگ، تجزیہ) کے لئے، Service Locator جائز ہو سکتا ہے۔ فیصلہ سیاق و سباق پر منحصر ہے: ٹیم کے ساتھ پروڈکشن ایپلیکیشن کے لئے — DI، پروٹوٹائپ پر ایک ڈویلپر کے لئے — Service Locator۔

Service Locator DI کنٹینر سے کیسے مختلف ہے؟

DI کنٹینر (Dagger, Swinject) خود بخود ایک آبجیکٹ میں انحصار انجیکٹ کرتا ہے — آبجیکٹ کنٹینر کے وجود سے بے خبر ہوتا ہے۔ Service Locator — آبجیکٹ خود رجسٹری سے انحصار کی درخواست کرتا ہے۔ DI کنٹینر IoC اصول کی پیروی کرتا ہے؛ Service Locator اس کی خلاف ورزی کرتا ہے: آبجیکٹ اپنے انحصار کے حصول کا خود انتظام کرتا ہے۔ DI کنٹینر آبجیکٹ بنانے سے پہلے (کنسٹرکٹر کے ذریعے) کام کرتا ہے، Service Locator — کوڈ میں کہیں بھی۔

iOS میں Service Locator کب استعمال کریں؟

Service Locator iOS میں اس کے لئے جائز ہے: عالمی خدمات (Analytics, Logger, Crashlytics)، پروٹوٹائپ جہاں Swinject کی ترتیب ضرورت سے زیادہ ہے، اور بڑے وراثتی ماڈیولز کے یونٹ ٹیسٹ کے لئے۔ نئے iOS منصوبوں کے لئے، Swinject یا کنسٹرکٹر کے ذریعے دستی DI تجویز کیا جاتا ہے۔ @Environment کے ساتھ SwiftUI — بھی DI کی ایک شکل ہے، جو Service Locator سے بچتی ہے۔

Service Locator میں دوڑ کے حالات سے کیسے بچیں؟

تھریڈ سیف کلیکشن استعمال کریں (Swift میں NSLock، Kotlin میں ConcurrentHashMap)۔ ٹیسٹوں کے لئے — ThreadLocal یا دائرہ کار والا لوکیٹر۔ متبادل: async-local ذخیرہ — خدمات ایک coroutine/actor سے منسلک۔ بہترین حل متوازی ٹیسٹوں کے لئے Service Locator سے بچنا اور ہر ٹیسٹ کے لئے واضح آبجیکٹ تخلیق کے ساتھ DI استعمال کرنا ہے۔

کیا Service Locator ایک سنگلٹن ہے؟

Service Locator عام طور پر Singleton کے طور پر نافذ کیا جاتا ہے، لیکن یہ لازمی نہیں ہے۔ آپ ایک ماڈیول کے لئے لوکیٹر مثال بنا سکتے ہیں (فیچر اسکوپڈ لوکیٹر) اور اسے کنسٹرکٹر کے ذریعے منتقل کر سکتے ہیں۔ فیچر اسکوپڈ لوکیٹر عالمی حالت کا مسئلہ حل کرتا ہے لیکن چھپے ہوئے انحصار کا مسئلہ حل نہیں کرتا۔ اس پیٹرن کو Ambient Context یا Scoped Locator کہا جاتا ہے۔

خلاصہ

  • Service Locator — جامد رسائی کے ذریعے خدمات حاصل کرنے کے لئے ایک مرکزی رجسٹری
  • چھپے ہوئے انحصار — بنیادی خرابی: انحصار کلاس کے دستخط میں نظر نہیں آتے
  • جانچ — عالمی حالت اور دوبارہ ترتیب کی ضرورت کی وجہ سے DI سے زیادہ پیچیدہ
  • Swift/Kotlin — تھریڈ سیف لغت اور reified generics کے ذریعے نفاذ
  • متبادل — DI (Dagger Hilt, Swinject) — پروڈکشن منصوبوں کے لئے معیار
  • جائز — پروٹوٹائپس، عالمی خدمات اور وراثتی منصوبوں میں

ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے

IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔

پروجیکٹ پر بحث کریں

مزید پڑھیں