परिचय: Dependency Injection क्या है और इसकी ज़रूरत क्यों है?
\nकल्पना करें कि आप एक घर बना रहे हैं। आपको ईंट, सीमेंट और औज़ार चाहिए। हर बार गोदाम में दौड़ने के बजाय, आप सारी सामग्री सीधे निर्माण स्थल पर मंगवा लेते हैं। Dependency Injection (DI) या निर्भरता इंजेक्शन आपके कोड के लिए बिल्कुल यही सिद्धांत है।
\nDependency Injection एक डिज़ाइन पैटर्न है जिसमें कोई ऑब्जेक्ट अपनी निर्भरताएँ (dependencies) खुद नहीं बनाता, बल्कि उन्हें बाहर से प्राप्त करता है। इससे कोड अधिक लचीला, टेस्ट करने योग्य और मेंटेन करने में आसान हो जाता है। इस लेख में हम Kotlin के उदाहरणों के साथ DI को समझेंगे - एक आधुनिक प्रोग्रामिंग भाषा जो इस पैटर्न को सीखने के लिए बहुत उपयुक्त है।
\n\nसमस्या: DI के बिना कोड कमज़ोर क्यों हो जाता है?
\nएक सामान्य उदाहरण देखते हैं। मान लीजिए हमारे पास UserService क्लास है जो ईमेल नोटिफिकेशन भेजता है। DI के बिना यह इस तरह दिखता है:
class EmailService {\n fun sendEmail(to: String, message: String) {\n println("$to पर ईमेल भेजा जा रहा है: $message")\n }\n}\n\nclass UserService {\n private val emailService = EmailService() // कड़ी निर्भरता!\n\n fun notifyUser(email: String) {\n emailService.sendEmail(email, "नमस्ते, उपयोगकर्ता!")\n }\n}\nसमस्या क्या है? UserService EmailService की एक विशेष इम्प्लीमेंटेशन से कसकर बंधा हुआ है। अगर आप यह करना चाहें:
\n- \n
- EmailService को SmsService से बदलना; \n
- लॉगिंग जोड़ना; \n
- मॉक ऑब्जेक्ट के साथ यूनिट टेस्ट लिखना, \n
— तो आपको UserService का कोड बदलना होगा। यह Open/Closed सिद्धांत का उल्लंघन करता है और टेस्टिंग को मुश्किल बनाता है।
\n\nसमाधान: Kotlin में मैन्युअल Dependency Injection
\nDI लागू करने का सबसे आसान तरीका है कंस्ट्रक्टर के माध्यम से निर्भरता पास करना। Kotlin में यह बहुत संक्षिप्त रूप से किया जा सकता है:
\n// 1. इंटरफ़ेस (एब्स्ट्रैक्शन) बनाएँ\ninterface NotificationService {\n fun send(to: String, message: String)\n}\n\n// 2. विशिष्ट सेवाओं को लागू करें\nclass EmailService : NotificationService {\n override fun send(to: String, message: String) {\n println("$to पर ईमेल: $message")\n }\n}\n\nclass SmsService : NotificationService {\n override fun send(to: String, message: String) {\n println("$to पर SMS: $message")\n }\n}\n\n// 3. कंस्ट्रक्टर के माध्यम से निर्भरता इंजेक्ट करें\nclass UserService(private val notificationService: NotificationService) {\n fun notifyUser(email: String) {\n notificationService.send(email, "नमस्ते!")\n }\n}\n\n// 4. उपयोग\nfun main() {\n val emailService = EmailService()\n val userService = UserService(emailService) // इंजेक्शन!\n userService.notifyUser("user@example.com")\n}\nइस दृष्टिकोण के लाभ:
\n- \n
- लचीलापन:
EmailServiceको आसानी सेSmsServiceसे बदला जा सकता है; \n - टेस्ट करने की क्षमता: टेस्ट में आप मॉक ऑब्जेक्ट पास कर सकते हैं: \n
class MockNotificationService : NotificationService {\n var lastMessage: String? = null\n override fun send(to: String, message: String) {\n lastMessage = message\n }\n}\n\nfun test() {\n val mock = MockNotificationService()\n val userService = UserService(mock)\n userService.notifyUser("test@test.com")\n assert(mock.lastMessage == "नमस्ते!")\n}\n\nDI फ्रेमवर्क: Kotlin के लिए Koin और Dagger
\nमैन्युअल DI के साथ, अगर प्रोजेक्ट बड़ा है, तो आपको बहुत सारा बॉयलरप्लेट कोड लिखना पड़ता है। यहाँ DI फ्रेमवर्क काम आते हैं। Kotlin की दुनिया में दो लोकप्रिय हैं: Koin (सरल) और Dagger/Hilt (शक्तिशाली)।
\n\nKoin: सादगी और गति
\nKoin एक हल्का DI फ्रेमवर्क है जो Kotlin में लिखा गया है। इसमें कोड जनरेशन की ज़रूरत नहीं है और यह