Service Locator একটি আর্কিটেকচারাল প্যাটার্ন যা সার্ভিসের একটি কেন্দ্রীয় রেজিস্ট্রি প্রদান করে। ক্লায়েন্ট কোড একটি স্ট্যাটিক লোকেটরের মাধ্যমে সার্ভিস অনুরোধ করে, এটি সরাসরি তৈরি না করে বা কনস্ট্রাক্টরের মাধ্যমে না পেয়ে। Service Locator প্রায়শই Dependency Injection-এর বিকল্প হিসেবে বিবেচিত হয়: এটি বাস্তবায়নে সহজ, কিন্তু নির্ভরতা লুকিয়ে রাখে এবং পরীক্ষা জটিল করে তোলে। প্যাটার্নটি সার্ভিস নিবন্ধন এবং সমাধান সহ একটি বিশ্বব্যাপী Singleton ক্লাসের মাধ্যমে বাস্তবায়িত হয়। আরও বিস্তারিত — Martin Fowler-এর 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 — স্ট্যাটিক প্রপার্টি এবং থ্রেড-সেফ অভিধানের মাধ্যমে বাস্তবায়ন। ObjectIdentifier(Protocol.self) কে কী এবং ফ্যাক্টরি (() -> Any) কে মান হিসেবে ব্যবহার করে একটি রেজিস্ট্রি ব্যবহৃত হয়। অলস আরম্ভীকরণ (lazy var) মানক অভ্যাস: প্রথম অনুরোধে সার্ভিস তৈরি হয়। Swift-এ resolve-এর সময় স্পষ্ট টাইপ রূপান্তর প্রয়োজন: 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()
}
}
// নিবন্ধন
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 — টাইপ সুরক্ষার জন্য inline reified ফাংশন সহ object (Singleton) এর মাধ্যমে একটি কমপ্যাক্ট বাস্তবায়ন। Kotlin একটি সংক্ষিপ্ত লোকেটার তৈরি করতে দেয়: val service by locator একটি ডেলিগেট সহ, কোড পরিষ্কার করে। Reified generics () ObjectIdentifier প্রতিস্থাপন করে — টাইপ জেনেরিক থেকে পাওয়া যায়। Kotlin লোকেটার প্রায়ই স্পষ্ট লক ছাড়া থ্রেড সুরক্ষার জন্য ConcurrentHashMap ব্যবহার করে।
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 ছাড়া মক সার্ভিস সহ একটি সাধারণ ServiceLocator স্টাব। সুবিধা: পরীক্ষার জন্য Dagger কম্পাইলেশনের জন্য অপেক্ষা করার প্রয়োজন নেই। অসুবিধা: যদি আপনি পরীক্ষায় লোকেটার ওভাররাইড করতে ভুলে যান, পরীক্ষাগুলি প্রোডাকশন সার্ভিস ব্যবহার করে।
নির্ভরতার স্পষ্টতা — প্রধান পার্থক্য। DI স্পষ্টভাবে নির্ভরতা ঘোষণা করে: init(service: ServiceProtocol) — যেকোনো IDE ক্লাসের নির্ভরতা দেখায়। Service Locator সেগুলি লুকায়: dependencies = ServiceLocator.resolve() — পদ্ধতির ভিতরে লুকানো। DI-র সাথে, আপনি অবিলম্বে সমস্ত ক্লাস নির্ভরতা দেখতে পারেন; Service Locator-এর সাথে, আপনাকে পুরো ক্লাস বডি পড়তে হবে। এটি Service Locator কোডকে কম অনুমানযোগ্য করে তোলে: রেজিস্ট্রি পরিবর্তন করলে লোকেটার ব্যবহার করে এমন যেকোনো ক্লাস ভেঙে যেতে পারে।
| বৈশিষ্ট্য | Service Locator | Dependency Injection |
|---|---|---|
| নির্ভরতা দৃশ্যমানতা | পদ্ধতি বডিতে লুকানো | কনস্ট্রাক্টরে স্পষ্ট |
| পরীক্ষা | বিশ্বব্যাপী রেজিস্ট্রি কনফিগার করুন | কনস্ট্রাক্টরে মক |
| মডুলারিটি | বিশ্বব্যাপী রেজিস্ট্রি — মডুলার নয় | পৃথক কন্টেইনার সহ মডিউল |
| জটিলতা | সহজ বাস্তবায়ন, 50-100 লাইন | Dagger/Swinject প্রয়োজন |
| বাজার-যেতে-সময় | দ্রুত শুরু | কন্টেইনার সেটআপ প্রয়োজন |
কখন Service Locator ন্যায্য — প্রোটোটাইপ এবং MVP (কনফিগারেশন ছাড়া দ্রুত শুরু)। লিগ্যাসি প্রকল্প যেখানে DI ফ্রেমওয়ার্ক যোগ করা অসম্ভব (জটিল বিল্ড, লিন্টার সীমাবদ্ধতা)। ইন্সট্রুমেন্টেশন লাইব্রেরি (লগিং, ক্র্যাশ রিপোর্টিং) — তারা ইতিমধ্যেই বিশ্বব্যাপী। 3+ ডেভেলপারের টিম সহ প্রোডাকশন অ্যাপ্লিকেশনের জন্য, DI সুপারিশ করা হয়: স্পষ্ট নির্ভরতা রিফ্যাক্টরিংয়ের সময় ত্রুটির সংখ্যা কমায় এবং নতুন ডেভেলপারদের অন্তর্ভুক্ত করা সহজ করে।
লুকানো নির্ভরতা — একটি ক্লাস যা পদ্ধতির ভিতরে ServiceLocator.resolve() ব্যবহার করে, স্ট্যাটিকভাবে বিশ্লেষণ করা যায় না। IDE নির্ভরতা দেখায় না, কম্পাইলার সার্ভিস নিবন্ধিত কিনা তা পরীক্ষা করে না। "Service not registered" ত্রুটি শুধুমাত্র রানটাইমে ঘটে। রিফ্যাক্টরিং বিপজ্জনক হয়ে ওঠে: রেজিস্ট্রি থেকে সার্ভিস অপসারণ অ্যাপ্লিকেশনের যেকোনো ক্লাস ভেঙে দিতে পারে। DI এই সমস্যাটি কম্পাইল-টাইম চেক (Dagger) বা স্পষ্ট কনস্ট্রাক্টরের মাধ্যমে সমাধান করে।
পরীক্ষার সমস্যা — প্রতিটি পরীক্ষাকে সমস্ত নির্ভরতা সহ ServiceLocator.shared কনফিগার করতে হবে। পরীক্ষার পরে — অবস্থা রিসেট করুন। সমান্তরাল পরীক্ষা নির্বাহের সময়, ServiceLocator.shared-এর বিশ্বব্যাপী অবস্থা রেস কন্ডিশনের দিকে নিয়ে যায়: একটি পরীক্ষা মক নিবন্ধন করে, অন্যটি অন্যের মক পায়। সমাধান: স্কোপড লোকেটার (প্রতি পরীক্ষায় একটি) বা ThreadLocal। DI প্রথম থেকেই এই সমস্যার সমাধান করে: প্রতিটি পরীক্ষা মক নির্ভরতা সহ নিজস্ব ইন্সট্যান্স তৈরি করে।
// 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 ন্যায্য হতে পারে। সিদ্ধান্ত প্রসঙ্গের উপর নির্ভর করে: টিম সহ প্রোডাকশন অ্যাপ্লিকেশনের জন্য — DI, প্রোটোটাইপে একক ডেভেলপারের জন্য — Service Locator।
DI কন্টেইনার (Dagger, Swinject) স্বয়ংক্রিয়ভাবে একটি অবজেক্টে নির্ভরতা ইনজেক্ট করে — অবজেক্ট কন্টেইনারের অস্তিত্ব সম্পর্কে জানে না। Service Locator — অবজেক্ট নিজেই রেজিস্ট্রি থেকে নির্ভরতা অনুরোধ করে। DI কন্টেইনার IoC নীতি অনুসরণ করে; Service Locator এটি লঙ্ঘন করে: অবজেক্ট নিজেই তার নির্ভরতা প্রাপ্তি পরিচালনা করে। DI কন্টেইনার অবজেক্ট তৈরির আগে (কনস্ট্রাক্টরের মাধ্যমে) কাজ করে, Service Locator — কোডের যেকোনো জায়গায়।
iOS-এ Service Locator ন্যায্য: বিশ্বব্যাপী সার্ভিস (Analytics, Logger, Crashlytics), প্রোটোটাইপ যেখানে Swinject সেটআপ অপ্রয়োজনীয়, এবং বড় লিগ্যাসি মডিউলের ইউনিট টেস্টের জন্য। নতুন iOS প্রকল্পের জন্য, Swinject বা কনস্ট্রাক্টরের মাধ্যমে ম্যানুয়াল DI সুপারিশ করা হয়। SwiftUI @Environment সহ — DI-এর একটি রূপ, Service Locator এড়িয়ে চলে।
থ্রেড-সেফ কালেকশন ব্যবহার করুন (Swift-এ NSLock, Kotlin-এ ConcurrentHashMap)। পরীক্ষার জন্য — ThreadLocal বা স্কোপড লোকেটার। বিকল্প: async-local স্টোরেজ — সার্ভিস coroutine/actor-এর সাথে আবদ্ধ। সর্বোত্তম সমাধান হল সমান্তরাল পরীক্ষার জন্য Service Locator এড়ানো এবং প্রতিটি পরীক্ষার জন্য স্পষ্ট অবজেক্ট তৈরি সহ DI ব্যবহার করা।
Service Locator সাধারণত Singleton হিসেবে বাস্তবায়িত হয়, তবে এটি বাধ্যতামূলক নয়। আপনি একটি মডিউলের জন্য লোকেটার ইন্সট্যান্স তৈরি করতে পারেন (ফিচার-স্কোপড লোকেটার) এবং এটি কনস্ট্রাক্টরের মাধ্যমে পাস করতে পারেন। ফিচার-স্কোপড লোকেটার বিশ্বব্যাপী অবস্থা সমস্যা সমাধান করে কিন্তু লুকানো নির্ভরতার সমস্যা সমাধান করে না। এই প্যাটার্নটিকে Ambient Context বা Scoped Locator বলা হয়।
সারাংশ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।