डिपेंडेंसी इंजेक्शन (DI) एक तकनीक है जिसमें ऑब्जेक्ट अपनी निर्भरताएँ बाहर से प्राप्त करता है, बजाय उन्हें स्वयं बनाने के। DI, IoC (इनवर्ज़न ऑफ़ कंट्रोल) सिद्धांत का कार्यान्वयन है और Dagger, Hilt और Swinject का आधार है। डिपेंडेंसी इंजेक्शन कोड युग्मन को कम करता है, परीक्षण को सरल बनाता है और आर्किटेक्चर को लचीला बनाता है। Android में, DI Google के Dagger Hilt के माध्यम से मानक है, iOS में — Swinject या मैनुअल इंजेक्शन के माध्यम से। अधिक जानकारी के लिए Android DI गाइड देखें।
मुख्य बातें
डिपेंडेंसी इंजेक्शन एक तकनीक है जिसमें एक ऑब्जेक्ट कंस्ट्रक्टर, सेटर या इंटरफ़ेस के माध्यम से निर्भरताएँ (सेवाएँ, रिपॉजिटरी, कॉन्फ़िगरेशन) प्राप्त करता है, बजाय उन्हें new के साथ स्वयं बनाने के। DI का लक्ष्य वर्गों के बीच युग्मन को कम करना है। यदि कोई वर्ग स्वयं निर्भरताएँ बनाता है, तो वह विशिष्ट कार्यान्वयनों से मजबूती से जुड़ जाता है, जिससे परीक्षण और संशोधन कठिन हो जाता है। DI के साथ, वर्ग एक अमूर्तता (प्रोटोकॉल/इंटरफ़ेस) के साथ काम करता है, और ठोस कार्यान्वयन बाहर से प्रदान किया जाता है।
इंजेक्शन के तीन तरीके — कंस्ट्रक्टर इंजेक्शन (init/constructor के माध्यम से), सेटर इंजेक्शन (प्रॉपर्टी/setter के माध्यम से), इंटरफ़ेस इंजेक्शन (इंटरफ़ेस विधि के माध्यम से)। कंस्ट्रक्टर इंजेक्शन पसंदीदा तरीका है: निर्भरताएँ हस्ताक्षर में स्पष्ट रूप से दिखाई देती हैं, और ऑब्जेक्ट हमेशा मान्य स्थिति में बनाया जाता है। सेटर इंजेक्शन डिफ़ॉल्ट मान वाली वैकल्पिक निर्भरताओं के लिए उपयोग किया जाता है। इंटरफ़ेस इंजेक्शन दुर्लभ है, मुख्य रूप से DI कंटेनरों के लिए।
| DI प्रकार | तरीका | कब उपयोग करें | उदाहरण |
|---|---|---|---|
| कंस्ट्रक्टर | इनिशियलाइज़र पैरामीटर | अनिवार्य निर्भरताएँ | init(service: ServiceProtocol) |
| प्रॉपर्टी | वर्ग प्रॉपर्टी | वैकल्पिक निर्भरताएँ | var service: ServiceProtocol? |
| विधि | विधि पैरामीटर | अस्थायी निर्भरताएँ | func doWork(with service: Service) |
DI कंटेनर — एक लाइब्रेरी जो निर्भरताओं के निर्माण और जीवनचक्र का प्रबंधन करती है। कंटेनर में प्रकार पंजीकरण (प्रत्येक अमूर्त प्रकार एक ठोस कार्यान्वयन से मैप किया गया) और हल की गई निर्भरताओं वाली वस्तुएँ बनाने के लिए फ़ैक्टरी होती है। Android में — Dagger/Hilt, iOS में — Swinject, Needle, Dip। कंटेनर स्कोप प्रबंधित कर सकता है: सिंगलटन (प्रति एप्लिकेशन एक इंस्टेंस), फ़ीचर स्कोप (प्रति स्क्रीन) या प्रत्येक अनुरोध पर नई वस्तु।
Dagger Hilt Google के Dagger के ऊपर एक रैपर है, जो Android के लिए मानक DI लाइब्रेरी है। Hilt Dagger को सरल बनाता है: यह मैनुअल कंपोनेंट निर्माण को हटाता है, @HiltAndroidApp, @AndroidEntryPoint और @Module जोड़ता है। Hilt Android जीवनचक्र के साथ एकीकृत होता है: ViewModel, Activity, Fragment, Service, BroadcastReceiver एनोटेशन के माध्यम से निर्भरताएँ प्राप्त कर सकते हैं। कोड जनरेशन कंपाइल समय पर होता है — Dagger कंपोनेंट कार्यान्वयन उत्पन्न करता है, जो शून्य runtime ओवरहेड देता है।
// एप्लिकेशन क्लास
@HiltAndroidApp
class MyApp : Application()
// मॉड्यूल — निर्धारित करता है कि निर्भरताएँ कैसे बनाएँ
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient = OkHttpClient.Builder().build()
@Provides
@Singleton
fun provideApiService(client: OkHttpClient): ApiService {
return Retrofit.Builder()
.baseUrl("https://api.example.com")
.client(client)
.build()
.create(ApiService::class.java)
}
}
// ViewModel कंस्ट्रक्टर के माध्यम से निर्भरता प्राप्त करता है
@HiltViewModel
class MainViewModel @Inject constructor(
private val apiService: ApiService
) : ViewModel() {
private val _state = MutableStateFlow(MainState.Loading)
val state: StateFlow<MainState> = _state.asStateFlow()
fun loadData() {
viewModelScope.launch {
_state.value = MainState.Success(apiService.getData())
}
}
}
// Activity — @AndroidEntryPoint DI को सक्षम करता है
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
}
Dagger कंपोनेंट और दृश्यता क्षेत्र — @Singleton (संपूर्ण एप्लिकेशन), @ActivityScoped (Activity के अनुसार), @FragmentScoped (Fragment के अनुसार), @ViewModelScoped (ViewModel के अनुसार)। स्कोप का चुनाव ऑब्जेक्ट के जीवनकाल को निर्धारित करता है। @Singleton — प्रति प्रक्रिया एक इंस्टेंस, OkHttpClient और डेटाबेस के लिए उपयुक्त। @ActivityScoped — ऑब्जेक्ट तब तक जीवित रहता है जब तक Activity जीवित है, स्क्रीन स्तर की निर्भरताओं के लिए। @ViewModelScoped — Hilt 2.45+ में नया, ऑब्जेक्ट तब तक जीवित रहता है जब तक ViewModel जीवित है, कोरूटीन स्कोप के लिए सुविधाजनक।
Swinject iOS के लिए एक लोकप्रिय ओपन-सोर्स DI फ्रेमवर्क है। Swinject Container, Assemblies और विभिन्न स्कोप प्रदान करता है। Dagger के विपरीत, Swinject रनटाइम पर काम करता है — निर्भरताएँ कोड जनरेशन के बिना गतिशील रूप से हल की जाती हैं। यह Swinject को सेटअप करना आसान बनाता है, लेकिन डीबग करना अधिक कठिन है: अनसुलझी निर्भरता त्रुटि केवल रनटाइम पर दिखाई देती है। Swinject कंस्ट्रक्टर इंजेक्शन, प्रॉपर्टी इंजेक्शन और मेथड इंजेक्शन का समर्थन करता है।
import Swinject
// Assembly — पंजीकरणों का समूह
class NetworkAssembly: Assembly {
func assemble(container: Container) {
container.register(NetworkServiceProtocol.self) { _ in
NetworkService()
}.inObjectScope(.container) // singleton
container.register(UserRepositoryProtocol.self) { r in
UserRepository(
networkService: r.resolve(NetworkServiceProtocol.self)!
)
}
}
}
// ViewModel कंस्ट्रक्टर इंजेक्शन के माध्यम से
class ProfileViewModel: ObservableObject {
private let repository: UserRepositoryProtocol
init(repository: UserRepositoryProtocol) {
self.repository = repository
}
@Published var user: User?
func loadUser() {
repository.fetchUser { [weak self] user in
self?.user = user
}
}
}
// AppDelegate या ऐप में DI सेटअप
let assembler = Assembler([NetworkAssembly(), ServiceAssembly()])
let viewModel = assembler.resolver.resolve(ProfileViewModel.self)!
// UIKit ViewController के लिए प्रॉपर्टी इंजेक्शन
container.register(UserViewController.self) { r in
let vc = UserViewController()
vc.viewModel = r.resolve(ProfileViewModel.self)
return vc
}
Swinject स्कोप — .transient (हर बार नई वस्तु), .container (प्रति कंटेनर सिंगलटन), .graph (डिफ़ॉल्ट — ऑब्जेक्ट एक ही निर्भरता ग्राफ के भीतर साझा किया जाता है)। iOS एप्लिकेशन के लिए, .container और .transient पर्याप्त हैं। Swinject Assembler का भी समर्थन करता है — मॉड्यूलर आर्किटेक्चर के लिए Assembly का समूहीकरण। परीक्षण के लिए, Assembly को MockAssembly से बदल दिया जाता है, जो प्रोडक्शन कोड को बदले बिना निर्भरता प्रतिस्थापन को सक्षम करता है।
DI बनाम Service Locator — दोनों पैटर्न निर्भरता प्रबंधन की समस्या को हल करते हैं, लेकिन अलग-अलग तरीकों से। DI ऑब्जेक्ट में निर्भरताएँ इंजेक्ट करता है; Service Locator एक वैश्विक रजिस्ट्री प्रदान करता है जिससे ऑब्जेक्ट स्वयं निर्भरताएँ माँगता है। DI कंस्ट्रक्टर (या सेटर) के माध्यम से निर्भरताओं को स्पष्ट रूप से घोषित करता है। Service Locator निर्भरताओं को छिपाता है — वे विधि के अंदर माँगी जाती हैं, जो हस्ताक्षर को कम सूचनात्मक बनाता है। DI का परीक्षण आसान है: कंस्ट्रक्टर में mock पास करना पर्याप्त है। Service Locator को प्रत्येक परीक्षण के लिए वैश्विक रजिस्ट्री सेटअप की आवश्यकता होती है।
| विशेषता | डिपेंडेंसी इंजेक्शन | Service Locator | मैनुअल इंजेक्शन |
|---|---|---|---|
| निर्भरताओं की स्पष्टता | कंस्ट्रक्टर में | विधि के मुख्य भाग में छिपी | स्पष्ट |
| परीक्षण | कंस्ट्रक्टर में Mock | Locator सेटअप | कंस्ट्रक्टर में Mock |
| सेटअप जटिलता | DI कंटेनर आवश्यक | वैश्विक रजिस्ट्री | मैनुअल निर्माण |
| Runtime ओवरहेड | Dagger — कंपाइल-टाइम | Runtime लुकअप | कोई नहीं |
DI बनाम मैनुअल इंजेक्शन — DI कंटेनर के बिना, निर्भरताएँ फ़ैक्टरियों या AppDelegate में मैन्युअल रूप से बनाई जाती हैं। 5-10 वर्गों के लिए, मैनुअल इंजेक्शन सरल है — Dagger या Swinject सीखने की आवश्यकता नहीं है। 50+ वर्गों के लिए, मैनुअल इंजेक्शन समस्याग्रस्त हो जाता है: 5-6 मापदंडों वाले कंस्ट्रक्टर, जटिल निर्माण क्रम, कोड दोहराव। DI कंटेनर इन प्रक्रियाओं को स्वचालित करता है और स्पष्ट जीवनचक्र प्रबंधन प्रदान करता है। कंटेनर के बिना मैनुअल इंजेक्शन छोटे प्रोजेक्ट और प्रोटोटाइप के लिए एक अच्छा विकल्प है।
कंस्ट्रक्टर इंजेक्शन — मानक. अनिवार्य निर्भरताओं के लिए हमेशा कंस्ट्रक्टर इंजेक्शन का उपयोग करें। यह निर्भरताओं को स्पष्ट बनाता है और ऑब्जेक्ट हमेशा काम करने के लिए तैयार रहता है। सेटर इंजेक्शन — केवल वैकल्पिक निर्भरताओं के लिए (जैसे, delegate या listener)। इंटरफ़ेस इंजेक्शन — तब तक उपयोग न करें जब तक आप अपनी खुद की DI लाइब्रेरी नहीं लिख रहे हों। कंस्ट्रक्टर इंजेक्शन यह गारंटी देने का एकमात्र तरीका है कि ऑब्जेक्ट एक मान्य स्थिति में बनाया गया है।
एक वर्ग — एक जिम्मेदारी. यदि किसी वर्ग के कंस्ट्रक्टर को 5+ पैरामीटर चाहिए, तो संभवतः वर्ग एकल जिम्मेदारी सिद्धांत का उल्लंघन कर रहा है। वर्ग को कम निर्भरताओं वाले कई भागों में विभाजित करें। संकेत: यदि आप 6 अलग-अलग सेवाओं वाला ServiceManager वर्ग लिख रहे हैं — यह God Object एंटीपैटर्न है। व्यावसायिक तर्क को Use Cases (Interactors) में निकालें, प्रत्येक में 1-2 निर्भरताएँ हों।
// ❌ बुरा: 6 निर्भरताएँ — God Object
class ProfileViewModel @Inject constructor(
private val api: ApiService,
private val db: Database,
private val analytics: Analytics,
private val prefs: Preferences,
private val location: LocationProvider,
private val notification: NotificationManager
)
// ✅ अच्छा: 1-2 निर्भरताओं वाले Use Cases
class ProfileViewModel @Inject constructor(
private val loadProfileUseCase: LoadProfileUseCase,
private val trackAnalyticsUseCase: TrackAnalyticsUseCase
)
स्कोप और जीवनचक्र — प्रत्येक निर्भरता के लिए सही स्कोप चुनें। सिंगलटन: OkHttpClient, डेटाबेस, SharedPreferences। फ़ीचर स्कोप: रिपॉजिटरी, Use Cases (यदि वे स्टेटलेस हैं)। Transient: Value Objects, DateFormatter, पार्सर। स्कोपिंग त्रुटियाँ एक सामान्य समस्या हैं: स्क्रीन स्थिति संग्रहीत करने वाला सिंगलटन मेमोरी लीक का कारण बनता है। Android में, Hilt का @ActivityScoped इसे हल करता है; Swinject में, .container का सावधानी से उपयोग करें।
अक्सर पूछे जाने वाले प्रश्न
new वर्गों के बीच मजबूत युग्मन बनाता है — आप कोड बदले बिना कार्यान्वयन को नहीं बदल सकते। परीक्षण कठिन हो जाता है: आप वास्तविक सेवा के बजाय mock इंजेक्ट नहीं कर सकते। SRP का उल्लंघन होता है: वर्ग व्यावसायिक तर्क और निर्भरता निर्माण दोनों के लिए जिम्मेदार होता है। DI बाहर से निर्भरताएँ इंजेक्ट करके और अमूर्तताओं के साथ काम करके इन समस्याओं को हल करता है।
Dagger Hilt Google का मानक है, कोड जनरेशन के साथ कंपाइल-टाइम DI, बेहतर प्रदर्शन और Jetpack एकीकरण। Koin रनटाइम DI है, सेटअप में आसान, लेकिन धीमा और रनटाइम त्रुटियों के साथ। प्रोडक्शन प्रोजेक्ट के लिए Hilt चुनें। Koin प्रोटोटाइप और छोटे एप्लिकेशन के लिए उपयुक्त है।
नहीं। iOS के लिए उपलब्ध हैं: Swinject (runtime, लोकप्रिय), Needle (Uber से कंपाइल-टाइम), Dip (हल्का), Weaver (Sourcery-आधारित)। Apple कोई अंतर्निहित DI कंटेनर प्रदान नहीं करता, लेकिन init के माध्यम से मैनुअल इंजेक्शन मानक अभ्यास है। SwiftUI के लिए, बाहरी लाइब्रेरी के बिना Environment या @StateObject के माध्यम से मैनुअल DI अक्सर पर्याप्त होता है।
हाँ। कंस्ट्रक्टर के माध्यम से मैनुअल इंजेक्शन बिना फ्रेमवर्क के DI है। Service Locator बिना फ्रेमवर्क के एक विकल्प है। फ़ैक्टरियाँ और Factory Method भी DI के रूप हैं। फ्रेमवर्क (Dagger, Swinject) नियमित पंजीकरण और निर्भरता समाधान को स्वचालित करता है, लेकिन 10-20 वर्गों के लिए, मैनुअल DI पर्याप्त है।
DI एक तकनीक (टेम्पलेट) है जो इनवर्ज़न ऑफ़ कंट्रोल सिद्धांत को लागू करती है। GoF पैटर्न के विपरीत, DI की 3-4 वर्गों की सख्त संरचना नहीं होती है। DI निर्भरताओं को व्यवस्थित करने का एक तरीका है, न कि एक डिज़ाइन पैटर्न। DI कंटेनर (Dagger, Swinject) ऐसे फ्रेमवर्क हैं जो इस तकनीक को स्वचालित करते हैं।
सारांश
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।
यह भी पढ़ें