paint-brush
डैगर 2 के साथ निर्भरता इंजेक्शन: यह क्या है, मुख्य अवधारणाएँ, और अधिकद्वारा@dilip2882
404 रीडिंग
404 रीडिंग

डैगर 2 के साथ निर्भरता इंजेक्शन: यह क्या है, मुख्य अवधारणाएँ, और अधिक

द्वारा Dilip Patel21m2024/08/28
Read on Terminal Reader

बहुत लंबा; पढ़ने के लिए

डिपेंडेंसी इंजेक्शन (DI) एक डिज़ाइन पैटर्न है जिसका उपयोग इनवर्जन ऑफ़ कंट्रोल (IoC) को लागू करने के लिए किया जाता है। यह एक ऐसी तकनीक है जहाँ ऑब्जेक्ट बनाने की ज़िम्मेदारी कोड के दूसरे हिस्सों को हस्तांतरित की जाती है। यह ढीले युग्मन को बढ़ावा देता है, जिससे कोड अधिक मॉड्यूलर और प्रबंधित करने में आसान हो जाता है।
featured image - डैगर 2 के साथ निर्भरता इंजेक्शन: यह क्या है, मुख्य अवधारणाएँ, और अधिक
Dilip Patel HackerNoon profile picture
0-item


निर्भरता इंजेक्शन का परिचय

डिपेंडेंसी इंजेक्शन (DI) एक डिज़ाइन पैटर्न है जिसका उपयोग इनवर्जन ऑफ़ कंट्रोल (IoC) को लागू करने के लिए किया जाता है जहाँ निर्भरता बनाने और प्रबंधित करने का नियंत्रण एप्लिकेशन से बाहरी इकाई को स्थानांतरित किया जाता है। यह अधिक मॉड्यूलर, परीक्षण योग्य और रखरखाव योग्य कोड बनाने में मदद करता है। यह एक ऐसी तकनीक है जहाँ ऑब्जेक्ट बनाने की ज़िम्मेदारी कोड के अन्य भागों को हस्तांतरित की जाती है। यह ढीले युग्मन को बढ़ावा देता है, जिससे कोड अधिक मॉड्यूलर और प्रबंधित करने में आसान हो जाता है।

कक्षाओं को ठीक से काम करने के लिए अक्सर अन्य कक्षाओं के संदर्भों की आवश्यकता होती है। उदाहरण के लिए, एक Library क्लास पर विचार करें जिसके लिए Book क्लास की आवश्यकता होती है। इन आवश्यक कक्षाओं को निर्भरता के रूप में जाना जाता है। Library क्लास को संचालित करने के लिए Book क्लास के इंस्टेंस पर निर्भर रहना पड़ता है।

हाइपरस्किल.ऑर्ग

किसी क्लास के लिए आवश्यक ऑब्जेक्ट प्राप्त करने के तीन प्राथमिक तरीके हैं:

  1. स्व-निर्माण : क्लास अपनी निर्भरताएँ स्वयं बनाता है और आरंभ करता है। उदाहरण के लिए, Library क्लास Book क्लास का अपना इंस्टेंस बनाएगा और आरंभ करेगा।
  2. बाहरी पुनर्प्राप्ति : क्लास बाहरी स्रोत से निर्भरताएँ पुनर्प्राप्त करता है। कुछ Android API, जैसे कि Context getters और getSystemService() , इस तरह से काम करते हैं।
  3. निर्भरता इंजेक्शन : निर्भरताएँ क्लास को तब प्रदान की जाती हैं, जब इसे बनाया जाता है या उन विधियों के माध्यम से जिन्हें उनकी आवश्यकता होती है। उदाहरण के लिए, Library कंस्ट्रक्टर को पैरामीटर के रूप में Book इंस्टेंस प्राप्त होगा।

तीसरा विकल्प निर्भरता इंजेक्शन है! DI के साथ, आप क्लास की निर्भरताएँ प्रदान करते हैं बजाय इसके कि क्लास इंस्टेंस उन्हें स्वयं प्राप्त करे।

निर्भरता इंजेक्शन के बिना उदाहरण

DI के बिना, एक Library जो अपनी स्वयं की Book निर्भरता बनाती है, वह इस तरह दिखाई दे सकती है:

 class Library { private Book book = new Book(); void open() { book.read(); } } public class Main { public static void main(String[] args) { Library library = new Library(); library.open(); } }

यह DI का उदाहरण नहीं है क्योंकि Library क्लास अपनी खुद की Book बनाती है। यह समस्याग्रस्त हो सकता है क्योंकि:

  • टाइट कपलिंग : Library और Book टाइट कपल्ड हैं। Library का एक उदाहरण एक प्रकार की Book का उपयोग करता है, जिससे उपवर्गों या वैकल्पिक कार्यान्वयन का उपयोग करना मुश्किल हो जाता है।
  • परीक्षण कठिनाइयाँ : Book पर हार्ड निर्भरता परीक्षण को और अधिक चुनौतीपूर्ण बनाती है। Library Book के वास्तविक उदाहरण का उपयोग करती है, जिससे विभिन्न परीक्षण मामलों के लिए Book को संशोधित करने के लिए परीक्षण डबल्स के उपयोग को रोका जा सकता है।

निर्भरता इंजेक्शन के साथ उदाहरण

DI के साथ, Library के प्रत्येक इंस्टेंस द्वारा अपना स्वयं का Book ऑब्जेक्ट निर्मित करने के स्थान पर, उसे अपने कन्स्ट्रक्टर में पैरामीटर के रूप में Book ऑब्जेक्ट प्राप्त होता है:

 class Library { private Book book; Library(Book book) { this.book = book; } void open() { book.read(); } } public class Main { public static void main(String[] args) { Book book = new Book(); Library library = new Library(book); library.open(); }

मुख्य फ़ंक्शन Library उपयोग करता है। चूँकि Library Book पर निर्भर करती है, इसलिए ऐप Book का एक इंस्टेंस बनाता है और फिर Library का इंस्टेंस बनाने के लिए इसका उपयोग करता है। इस DI-आधारित दृष्टिकोण के लाभ ये हैं:

  • Library की पुनः प्रयोज्यता : आप Book के विभिन्न कार्यान्वयन को Library में पास कर सकते हैं। उदाहरण के लिए, आप Book का एक नया उपवर्ग परिभाषित कर सकते हैं जिसे EBook कहा जाता है जिसे आप Library द्वारा उपयोग करना चाहते हैं। DI के साथ, आप बस EBook का एक उदाहरण Library में पास करते हैं, और यह बिना किसी और बदलाव के काम करता है।
  • Library का आसान परीक्षण : आप विभिन्न परिदृश्यों का परीक्षण करने के लिए टेस्ट डबल्स में पास हो सकते हैं।

एक और DI उदाहरण

एक परिदृश्य पर विचार करें जहां एक NotificationService वर्ग एक Notification वर्ग पर निर्भर करता है। DI के बिना, NotificationService सीधे Notification का एक उदाहरण बनाती है, जिससे विभिन्न प्रकार की सूचनाओं का उपयोग करना या विभिन्न अधिसूचना कार्यान्वयन के साथ सेवा का परीक्षण करना मुश्किल हो जाता है।

DI को स्पष्ट करने के लिए, आइए इस उदाहरण को पुनः दोहराते हैं:

 interface Notification { void send(); } class EmailNotification implements Notification { @Override public void send() { // Send email notification } } class SMSNotification implements Notification { @Override public void send() { // Send SMS notification } } class NotificationService { void sendNotification(Notification notification) { notification.send(); } }

अब, NotificationService किसी विशिष्ट क्लास के बजाय Notification इंटरफ़ेस पर निर्भर करती है। यह Notification के विभिन्न कार्यान्वयनों को परस्पर उपयोग करने की अनुमति देता है। आप sendNotification विधि के माध्यम से वह कार्यान्वयन सेट कर सकते हैं जिसका आप उपयोग करना चाहते हैं:

 NotificationService service = new NotificationService(); service.sendNotification(new EmailNotification()); service.sendNotification(new SMSNotification());

एंड्रॉयड में निर्भरता इंजेक्शन के तरीके

डीआई के तीन मुख्य प्रकार हैं:

  1. विधि (इंटरफ़ेस) इंजेक्शन : निर्भरताएँ विधियों के माध्यम से पारित की जाती हैं जिन्हें क्लास इंटरफ़ेस या किसी अन्य क्लास के माध्यम से एक्सेस कर सकता है। पिछला उदाहरण विधि इंजेक्शन को दर्शाता है।
  2. कंस्ट्रक्टर इंजेक्शन : निर्भरताएं इसके कंस्ट्रक्टर के माध्यम से क्लास में पास की जाती हैं।
 class NotificationService { private final Notification notification; public NotificationService(Notification notification) { this.notification = notification; } public void sendNotification() { notification.send(); } } public class Main { public static void main(String[] args) { NotificationService service = new NotificationService(new EmailNotification()); service.sendNotification(); } }

3. फ़ील्ड इंजेक्शन (या सेटर इंजेक्शन) : कुछ Android फ़्रेमवर्क क्लास, जैसे कि एक्टिविटी और फ़्रैगमेंट, सिस्टम द्वारा इंस्टेंटिएट किए जाते हैं, इसलिए कंस्ट्रक्टर इंजेक्शन संभव नहीं है। फ़ील्ड इंजेक्शन के साथ, क्लास बनने के बाद निर्भरताएँ इंस्टेंटिएट की जाती हैं।

 class NotificationService { private Notification notification; public Notification getNotification() { return notification; } public void setNotification(Notification notification) { this.notification = notification; } public void sendNotification() { notification.send(); } } public class Main { public static void main(String[] args) { NotificationService service = new NotificationService(); service.setNotification(new EmailNotification()); service.sendNotification(); } }

4. विधि इंजेक्शन : निर्भरताएँ विधियों के माध्यम से प्रदान की जाती हैं, अक्सर @Inject एनोटेशन का उपयोग करके।

निर्भरता इंजेक्शन के लाभ

  • कक्षाएं अधिक पुन: प्रयोज्य हो जाती हैं और विशिष्ट कार्यान्वयन पर कम निर्भर होती हैं। यह नियंत्रण के व्युत्क्रम के कारण होता है, जहां कक्षाएं अब अपनी निर्भरताओं का प्रबंधन नहीं करती हैं, बल्कि किसी भी प्रदान की गई कॉन्फ़िगरेशन के साथ काम करती हैं।
  • निर्भरताएं API सतह का हिस्सा हैं और इन्हें ऑब्जेक्ट निर्माण या संकलन समय पर सत्यापित किया जा सकता है, जिससे रिफैक्टरिंग आसान हो जाती है।
  • चूंकि कोई वर्ग अपनी निर्भरताओं का प्रबंधन नहीं करता है, इसलिए विभिन्न परिदृश्यों को कवर करने के लिए परीक्षण के दौरान विभिन्न कार्यान्वयन पारित किए जा सकते हैं।

स्वचालित निर्भरता इंजेक्शन

पिछले उदाहरण में, आपने लाइब्रेरी का उपयोग किए बिना विभिन्न वर्गों की निर्भरताओं को मैन्युअल रूप से बनाया, प्रदान किया और प्रबंधित किया। इस दृष्टिकोण को मैन्युअल निर्भरता इंजेक्शन के रूप में जाना जाता है। जबकि यह सरल मामलों के लिए काम करता है, यह निर्भरता और वर्गों की संख्या बढ़ने पर बोझिल हो जाता है। मैन्युअल निर्भरता इंजेक्शन में कई कमियाँ हैं:

  • बॉयलरप्लेट कोड : बड़े अनुप्रयोगों के लिए, सभी निर्भरताओं को प्रबंधित करना और उन्हें सही ढंग से जोड़ना बहुत सारे दोहराव वाले कोड का परिणाम हो सकता है। एक बहु-स्तरित वास्तुकला में, शीर्ष परत के लिए एक ऑब्जेक्ट बनाने के लिए इसके नीचे की परतों के लिए सभी निर्भरताएँ प्रदान करना आवश्यक है। उदाहरण के लिए, एक कंप्यूटर बनाने के लिए, आपको एक सीपीयू, एक मदरबोर्ड, रैम और अन्य घटकों की आवश्यकता होती है; और एक सीपीयू को ट्रांजिस्टर और कैपेसिटर की आवश्यकता हो सकती है।
  • जटिल निर्भरता प्रबंधन : जब आप पहले से निर्भरता का निर्माण नहीं कर सकते हैं - जैसे कि आलसी आरंभीकरण या आपके ऐप में विशिष्ट प्रवाह के लिए ऑब्जेक्ट्स को स्कोप करना - तो आपको मेमोरी में अपनी निर्भरता के जीवनकाल को प्रबंधित करने के लिए एक कस्टम कंटेनर (या निर्भरता ग्राफ) लिखने और बनाए रखने की आवश्यकता होती है।

लाइब्रेरी आपके लिए निर्भरताएँ बनाकर और प्रदान करके इस प्रक्रिया को स्वचालित कर सकती हैं। ये लाइब्रेरी दो श्रेणियों में आती हैं:

  1. प्रतिबिंब-आधारित समाधान : ये रनटाइम पर निर्भरताओं को जोड़ते हैं।
  2. स्थैतिक समाधान : ये संकलन समय पर निर्भरताओं को जोड़ने के लिए कोड उत्पन्न करते हैं।

डैगर जावा, कोटलिन और एंड्रॉइड के लिए एक लोकप्रिय निर्भरता इंजेक्शन लाइब्रेरी है, जिसे Google द्वारा बनाए रखा जाता है। डैगर आपके लिए निर्भरता ग्राफ बनाकर और प्रबंधित करके आपके ऐप में DI को सरल बनाता है। यह पूरी तरह से स्थिर, संकलन-समय निर्भरता प्रदान करता है, जो Guice जैसे प्रतिबिंब-आधारित समाधानों से जुड़े कई विकास और प्रदर्शन मुद्दों को संबोधित करता है।

प्रतिबिंब-आधारित समाधान

ये फ्रेमवर्क रनटाइम पर निर्भरताओं को जोड़ते हैं:

  1. टूथपिक : एक रनटाइम DI फ्रेमवर्क जो निर्भरता को जोड़ने के लिए रिफ्लेक्शन का उपयोग करता है। इसे हल्का और तेज़ होने के लिए डिज़ाइन किया गया है, जो इसे Android एप्लिकेशन के लिए उपयुक्त बनाता है।

स्थैतिक समाधान

ये फ्रेमवर्क संकलन समय पर निर्भरताओं को जोड़ने के लिए कोड उत्पन्न करते हैं:

  1. हिल्ट : डैगर के शीर्ष पर निर्मित, हिल्ट एंड्रॉइड एप्लिकेशन में डैगर निर्भरता इंजेक्शन को शामिल करने का एक मानक तरीका प्रदान करता है । यह पूर्वनिर्धारित घटकों और स्कोप प्रदान करके डैगर के सेटअप और उपयोग को सरल बनाता है।
  2. कोइन : कोटलिन के लिए एक हल्का और सरल DI फ्रेमवर्क। कोइन निर्भरता को परिभाषित करने के लिए DSL का उपयोग करता है और इसे सेट अप करना और उपयोग करना आसान है।
  3. कोडेन : कोटलिन-आधारित DI फ्रेमवर्क जो उपयोग करने और समझने में आसान है। यह निर्भरताओं के प्रबंधन के लिए एक सरल और लचीला API प्रदान करता है।

निर्भरता इंजेक्शन के विकल्प

निर्भरता इंजेक्शन का एक विकल्प सर्विस लोकेटर पैटर्न है। यह डिज़ाइन पैटर्न कक्षाओं को उनकी ठोस निर्भरताओं से अलग करने में भी मदद करता है। आप सर्विस लोकेटर नामक एक क्लास बनाते हैं जो निर्भरताएँ बनाता और संग्रहीत करता है, उन्हें मांग पर प्रदान करता है।

 object ServiceLocator { fun getProcessor(): Processor = Processor() } class Computer { private val processor = ServiceLocator.getProcessor() fun start() { processor.run() } } fun main(args: Array<String>) { val computer = Computer() computer.start() }

सर्विस लोकेटर पैटर्न निर्भरता इंजेक्शन से इस मामले में अलग है कि निर्भरता का उपभोग कैसे किया जाता है। सर्विस लोकेटर पैटर्न के साथ, क्लास अपनी ज़रूरत के अनुसार निर्भरता का अनुरोध करते हैं; निर्भरता इंजेक्शन के साथ, ऐप सक्रिय रूप से आवश्यक ऑब्जेक्ट प्रदान करता है।

डैगर 2 क्या है?

डैगर 2 एंड्रॉयड के लिए एक लोकप्रिय DI फ्रेमवर्क है। यह संकलन-समय कोड जनरेशन का उपयोग करता है और अपने उच्च प्रदर्शन के लिए जाना जाता है। डैगर 2 निर्भरता को संभालने के लिए आवश्यक कोड उत्पन्न करके निर्भरता इंजेक्शन की प्रक्रिया को सरल बनाता है, बॉयलरप्लेट को कम करता है और दक्षता में सुधार करता है।

डैगर 2 एंड्रॉयड में निर्भरता इंजेक्शन के लिए एक एनोटेशन-आधारित लाइब्रेरी है। यहाँ मुख्य एनोटेशन और उनके उद्देश्य दिए गए हैं:

  • @मॉड्यूल : निर्भरता प्रदान करने वाली कक्षाओं को परिभाषित करने के लिए उपयोग किया जाता है। उदाहरण के लिए, एक मॉड्यूल रेट्रोफिट के लिए एक ApiClient प्रदान कर सकता है।
  • @Provides : निर्भरताएँ बनाने और वापस करने का तरीका निर्दिष्ट करने के लिए मॉड्यूल में विधियों को एनोटेट करता है।
  • @Inject : निर्भरताओं का अनुरोध करने के लिए उपयोग किया जाता है। फ़ील्ड, कंस्ट्रक्टर और विधियों पर लागू किया जा सकता है।
  • @Component : एक इंटरफ़ेस जो @Module और @Inject जोड़ता है। इसमें सभी मॉड्यूल शामिल हैं और यह एप्लिकेशन के लिए बिल्डर प्रदान करता है।
  • @सिंगलटन : यह सुनिश्चित करता है कि निर्भरता का एक ही उदाहरण बनाया जाए।
  • @Binds : निर्भरताएं प्रदान करने के लिए अमूर्त वर्गों में उपयोग किया जाता है, @Provides के समान लेकिन अधिक संक्षिप्त।

डैगर अवयव

डैगर आपके प्रोजेक्ट के लिए एक निर्भरता ग्राफ उत्पन्न कर सकता है, जिससे यह निर्धारित कर सकता है कि आवश्यकता पड़ने पर निर्भरता कहाँ से प्राप्त की जाए। इसे सक्षम करने के लिए, आपको एक इंटरफ़ेस बनाना होगा और इसे @Component के साथ एनोटेट करना होगा।

@Component इंटरफ़ेस के भीतर, आप उन विधियों को परिभाषित करते हैं जो आपके लिए आवश्यक कक्षाओं के उदाहरण लौटाते हैं (उदाहरण के लिए, BookRepository )। @Component एनोटेशन Dagger को एक कंटेनर बनाने का निर्देश देता है जिसमें वह सभी निर्भरताएँ हों जो उसके द्वारा प्रदर्शित प्रकारों को संतुष्ट करने के लिए आवश्यक हों। इस कंटेनर को Dagger घटक के रूप में जाना जाता है, और इसमें उन वस्तुओं का एक ग्राफ़ होता है जिन्हें Dagger उनकी निर्भरताओं के साथ प्रदान करना जानता है।


उदाहरण

आइये LibraryRepository से संबंधित एक उदाहरण पर विचार करें:

  1. कंस्ट्रक्टर को एनोटेट करें : LibraryRepository कंस्ट्रक्टर में @Inject एनोटेशन जोड़ें ताकि Dagger को पता चले कि LibraryRepository का इंस्टेंस कैसे बनाया जाए।
 public class LibraryRepository { private final LocalLibraryDataSource localDataSource; private final RemoteLibraryDataSource remoteDataSource; @Inject public LibraryRepository(LocalLibraryDataSource localDataSource, RemoteLibraryDataSource remoteDataSource) { this.localDataSource = localDataSource; this.remoteDataSource = remoteDataSource; } }

2. निर्भरताओं को एनोटेट करें : इसी तरह, निर्भरताओं ( LocalLibraryDataSource और RemoteLibraryDataSource ) के कंस्ट्रक्टर्स को एनोटेट करें ताकि डैगर को पता चले कि उन्हें कैसे बनाया जाए।

 public class LocalLibraryDataSource { @Inject public LocalLibraryDataSource() { // Initialization code } } public class RemoteLibraryDataSource { private final LibraryService libraryService; @Inject public RemoteLibraryDataSource(LibraryService libraryService) { this.libraryService = libraryService; } }

3. घटक को परिभाषित करें : निर्भरता ग्राफ को परिभाषित करने के लिए @Component के साथ एनोटेट किया गया एक इंटरफ़ेस बनाएं।

 @Component public interface ApplicationComponent { LibraryRepository getLibraryRepository(); }

जब आप प्रोजेक्ट बनाते हैं, तो Dagger आपके लिए ApplicationComponent इंटरफ़ेस का कार्यान्वयन उत्पन्न करता है, जिसे आमतौर पर DaggerApplicationComponent नाम दिया जाता है।

प्रयोग

अब आप उत्पन्न घटक का उपयोग अपने वर्गों के उदाहरण प्राप्त करने के लिए कर सकते हैं, जिनकी निर्भरताएं स्वचालित रूप से इंजेक्ट की जाती हैं:

 public class MainApplication extends Application { private ApplicationComponent applicationComponent; @Override public void onCreate() { super.onCreate(); applicationComponent = DaggerApplicationComponent.create(); } public ApplicationComponent getApplicationComponent() { return applicationComponent; } }

अपनी गतिविधि या खंड में, आप LibraryRepository इंस्टेंस को पुनः प्राप्त कर सकते हैं:

 public class MainActivity extends AppCompatActivity { @Inject LibraryRepository libraryRepository; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ((MainApplication) getApplication()).getApplicationComponent().inject(this); // Use the injected libraryRepository } }

डैगर 2 में प्रमुख अवधारणाएँ

1. मॉड्यूल
∘ मॉड्यूल की मुख्य अवधारणाएँ
∘ घटकों में मॉड्यूल शामिल करना
2. कार्यक्षेत्र
3. घटक
4. घटक निर्भरताएँ
5. रनटाइम बाइंडिंग

1. मॉड्यूल

डैगर 2 में मॉड्यूल @Module के साथ एनोटेट किए गए क्लास हैं जो घटकों को निर्भरता प्रदान करते हैं। उनमें @Provides या @Binds के साथ एनोटेट किए गए तरीके होते हैं जो यह निर्दिष्ट करते हैं कि निर्भरता कैसे बनाई और आपूर्ति की जाए। मॉड्यूल आपके एप्लिकेशन की ज़रूरत वाले ऑब्जेक्ट के निर्माण को व्यवस्थित और प्रबंधित करने के लिए आवश्यक हैं।

मॉड्यूल की मुख्य अवधारणाएँ

  1. @मॉड्यूल एनोटेशन: इस एनोटेशन का उपयोग किसी क्लास को डैगर मॉड्यूल के रूप में परिभाषित करने के लिए किया जाता है। मॉड्यूल क्लास में ऐसे तरीके होते हैं जो निर्भरता प्रदान करते हैं।
  2. @Provides एनोटेशन: इस एनोटेशन का उपयोग मॉड्यूल के भीतर विधियों पर यह इंगित करने के लिए किया जाता है कि विधि एक निश्चित निर्भरता प्रदान करती है। ये विधियाँ निर्भरता के उदाहरण बनाने और वापस करने के लिए जिम्मेदार हैं।
  3. @Binds एनोटेशन: इस एनोटेशन का उपयोग अमूर्त वर्गों में किसी कार्यान्वयन को इंटरफ़ेस से जोड़ने के लिए किया जाता है। यह @Provides से अधिक संक्षिप्त है और इसका उपयोग तब किया जाता है जब मॉड्यूल एक अमूर्त वर्ग होता है।

मॉड्यूल का उदाहरण

 @Module public class NetworkModule { @Provides @Singleton Retrofit provideRetrofit() { return new Retrofit.Builder() .baseUrl("https://api.example.com") .addConverterFactory(GsonConverterFactory.create()) .build(); } @Provides @Singleton OkHttpClient provideOkHttpClient() { return new OkHttpClient.Builder() .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) .build(); } }

इस उदाहरण में, NetworkModule @Module के साथ एनोटेट किया गया एक क्लास है। इसमें @Provides के साथ एनोटेट किए गए दो तरीके हैं जो Retrofit और OkHttpClient के इंस्टेंस बनाते हैं और लौटाते हैं।

@Binds का उपयोग करना

जब आपके पास एक इंटरफ़ेस और उसका कार्यान्वयन होता है, तो आप कार्यान्वयन को इंटरफ़ेस से जोड़ने के लिए @Binds उपयोग कर सकते हैं। यह @Provides उपयोग करने से ज़्यादा संक्षिप्त है।

 public interface ApiService { void fetchData(); } public class ApiServiceImpl implements ApiService { @Override public void fetchData() { // Implementation } } @Module public abstract class ApiModule { @Binds abstract ApiService bindApiService(ApiServiceImpl apiServiceImpl); }

इस उदाहरण में, ApiModule @Module के साथ एनोटेट किया गया एक अमूर्त वर्ग है। ApiServiceImpl ApiService से बाँधने के लिए bindApiService विधि को @Binds के साथ एनोटेट किया गया है।

मॉड्यूल को उनके द्वारा प्रदान की जाने वाली कार्यक्षमता के आधार पर व्यवस्थित किया जा सकता है। उदाहरण के लिए, आपके पास नेटवर्क संचालन, डेटाबेस संचालन और UI-संबंधित निर्भरताओं के लिए अलग-अलग मॉड्यूल हो सकते हैं।

उदाहरण:

  • NetworkModule : Retrofit और OkHttpClient जैसी नेटवर्क-संबंधित निर्भरताएं प्रदान करता है।
  • DatabaseModule : RoomDatabase जैसी डेटाबेस-संबंधित निर्भरताएं प्रदान करता है.
  • UIModule : ViewModel और Presenter जैसी UI-संबंधित निर्भरताएं प्रदान करता है।

घटकों में मॉड्यूल शामिल करना

मॉड्यूल को घटकों में शामिल किया जाता है ताकि उन वर्गों को निर्भरता प्रदान की जा सके जिन्हें उनकी आवश्यकता है। यहां बताया गया है कि आप इसे कैसे सेट कर सकते हैं:

एप्लीकेशन घटक.जावा:

 @Singleton @Component(modules = {NetworkModule.class, DatabaseModule.class}) public interface ApplicationComponent { void inject(MyApplication application); }

इस उदाहरण में, ApplicationComponent में अनुप्रयोग को निर्भरता प्रदान करने के लिए NetworkModule और DatabaseModule शामिल हैं।

2. कार्यक्षेत्र

डैगर 2 में स्कोप एनोटेशन हैं जो निर्भरता के जीवनचक्र को परिभाषित करते हैं। वे सुनिश्चित करते हैं कि निर्भरता का एक ही उदाहरण बनाया गया है और निर्दिष्ट दायरे में साझा किया गया है। यह मेमोरी को कुशलतापूर्वक प्रबंधित करने और यह सुनिश्चित करने में मदद करता है कि निर्भरता का पुन: उपयोग उचित रूप से किया जाए।

  • सिंगलटन स्कोप : अनुप्रयोग के संपूर्ण जीवनचक्र में निर्भरता का एकल उदाहरण सुनिश्चित करता है।
  • गतिविधि क्षेत्र : किसी गतिविधि के जीवनचक्र के भीतर निर्भरता का एक एकल उदाहरण सुनिश्चित करता है।
  • फ़्रैगमेंट स्कोप : फ़्रैगमेंट के जीवनचक्र के भीतर निर्भरता का एक एकल उदाहरण सुनिश्चित करता है।

1. सिंगलटन स्कोप

परिभाषा : @Singleton स्कोप यह सुनिश्चित करता है कि निर्भरता का एक एकल उदाहरण बनाया जाए और पूरे अनुप्रयोग के जीवनचक्र में साझा किया जाए।

इस क्षेत्र का उपयोग आम तौर पर उन निर्भरताओं के लिए किया जाता है जिन्हें पूरे अनुप्रयोग में साझा करने की आवश्यकता होती है, जैसे नेटवर्क क्लाइंट, डेटाबेस इंस्टेंस या साझा प्राथमिकताएं।

उदाहरण:

 @Singleton @Component(modules = {NetworkModule.class, DatabaseModule.class}) public interface ApplicationComponent { void inject(MyApplication application); }

इस उदाहरण में, @Singleton एनोटेशन यह सुनिश्चित करता है कि NetworkModule और DatabaseModule द्वारा प्रदान किए गए Retrofit और Database इंस्टेंस सिंगलटन हैं और पूरे अनुप्रयोग में साझा किए गए हैं।

2. गतिविधि का दायरा

परिभाषा : @ActivityScope (एक कस्टम स्कोप) यह सुनिश्चित करता है कि किसी गतिविधि के जीवनचक्र के भीतर निर्भरता का एक एकल उदाहरण बनाया और साझा किया जाए।

यह दायरा उन निर्भरताओं के लिए उपयोगी है जो किसी गतिविधि के लिए विशिष्ट हैं और जिन्हें प्रत्येक बार गतिविधि को पुनः बनाए जाने पर पुनः बनाया जाना चाहिए, जैसे प्रस्तुतकर्ता या दृश्य मॉडल।

उदाहरण :

 @Scope @Retention(RetentionPolicy.RUNTIME) public @interface ActivityScope { } @ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); }

इस उदाहरण में, @ActivityScope एनोटेशन यह सुनिश्चित करता है कि ActivityModule द्वारा प्रदान की गई निर्भरताएं गतिविधि के जीवनचक्र तक सीमित हैं।

3. खंड क्षेत्र

परिभाषा : @FragmentScope (एक अन्य कस्टम स्कोप) यह सुनिश्चित करता है कि निर्भरता का एक एकल उदाहरण फ्रेगमेंट के जीवनचक्र के भीतर बनाया और साझा किया जाए।

उपयोग मामला: यह दायरा उन निर्भरताओं के लिए उपयोगी है जो किसी फ्रेगमेंट के लिए विशिष्ट हैं और फ्रेगमेंट के पुनः निर्माण के समय हर बार उन्हें पुनः निर्मित किया जाना चाहिए, जैसे कि फ्रेगमेंट-विशिष्ट प्रस्तुतकर्ता या दृश्य मॉडल।

उदाहरण :

 @Scope @Retention(RetentionPolicy.RUNTIME) public @interface FragmentScope { } @FragmentScope @Component(dependencies = ActivityComponent.class, modules = FragmentModule.class) public interface FragmentComponent { void inject(MyFragment myFragment); }

इस उदाहरण में, @FragmentScope एनोटेशन यह सुनिश्चित करता है कि FragmentModule द्वारा प्रदान की गई निर्भरताएं फ्रेगमेंट के जीवनचक्र तक सीमित हैं।

3. घटक

घटक निर्भरताएँ एक घटक को दूसरे पर निर्भर रहने देती हैं, जिससे निर्भरताओं का पुनः उपयोग संभव हो जाता है। घटक निर्भरता के दो मुख्य प्रकार हैं:

  • अनुप्रयोग घटक : संपूर्ण अनुप्रयोग में आवश्यक निर्भरताएं प्रदान करता है।
  • गतिविधि घटक : किसी विशिष्ट गतिविधि के लिए आवश्यक निर्भरताएं प्रदान करता है।

1. अनुप्रयोग घटक

परिभाषा : एप्लिकेशन घटक निर्भरताएँ प्रदान करता है जो पूरे एप्लिकेशन में आवश्यक हैं। यह आमतौर पर @Singleton के साथ स्कोप किया जाता है ताकि यह सुनिश्चित किया जा सके कि निर्भरताएँ पूरे एप्लिकेशन में साझा की जाती हैं।

इस घटक का उपयोग उन निर्भरताओं के लिए किया जाता है जिन्हें वैश्विक रूप से उपलब्ध होना आवश्यक है, जैसे नेटवर्क क्लाइंट, डेटाबेस इंस्टेंस या साझा प्राथमिकताएं।

उदाहरण :

 @Singleton @Component(modules = {NetworkModule.class, DatabaseModule.class}) public interface ApplicationComponent { void inject(MyApplication application); }

इस उदाहरण में, ApplicationComponent Retrofit और Database इंस्टैंस प्रदान करने के लिए जिम्मेदार है, जिन्हें पूरे एप्लिकेशन में साझा किया जाता है।

2. गतिविधि घटक

परिभाषा : गतिविधि घटक ऐसी निर्भरताएँ प्रदान करता है जो किसी विशिष्ट गतिविधि के भीतर आवश्यक होती हैं। इसे आमतौर पर एक कस्टम स्कोप के साथ स्कोप किया जाता है, जैसे कि @ActivityScope , यह सुनिश्चित करने के लिए कि हर बार गतिविधि को फिर से बनाए जाने पर निर्भरताएँ फिर से बनाई जाएँ।

इस घटक का उपयोग उन निर्भरताओं के लिए किया जाता है जो किसी गतिविधि के लिए विशिष्ट होती हैं, जैसे प्रस्तुतकर्ता या दृश्य मॉडल।

उदाहरण :

 @ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); }

इस उदाहरण में, ActivityComponent , ApplicationComponent पर निर्भर करता है और MainActivity के लिए विशिष्ट निर्भरताएँ प्रदान करता है।

4. घटक निर्भरताएँ

घटक निर्भरताएँ एक घटक को दूसरे पर निर्भर रहने देती हैं, जिससे निर्भरताओं का पुनः उपयोग संभव हो जाता है। घटक निर्भरता के दो मुख्य प्रकार हैं:

  • उपघटक : एक उपघटक किसी अन्य घटक का संतान होता है और अपने मूल घटक की निर्भरताओं तक पहुंच सकता है।
  • निर्भरता विशेषता : यह एक घटक को उपघटक बने बिना किसी अन्य घटक पर निर्भर रहने की अनुमति देता है।

1. उपघटक:

एक उपघटक दूसरे घटक का बच्चा होता है और अपने पैरेंट की निर्भरताओं तक पहुँच सकता है। उपघटक पैरेंट घटक के भीतर परिभाषित किए जाते हैं और इसके दायरे को विरासत में ले सकते हैं।

उदाहरण :

 @ActivityScope @Subcomponent(modules = ActivityModule.class) public interface ActivitySubcomponent { void inject(MainActivity mainActivity); }

इस उदाहरण में, ActivitySubcomponent मूल घटक का उपघटक है और इसकी निर्भरताओं तक पहुँच सकता है।

2. निर्भरता विशेषता

यह किसी घटक को उपघटक बने बिना किसी अन्य घटक पर निर्भर रहने की अनुमति देता है। आश्रित घटक मूल घटक द्वारा प्रदान की गई निर्भरताओं तक पहुँच सकता है।

उदाहरण :

 @ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); }

इस उदाहरण में, ActivityComponent , ApplicationComponent पर निर्भर करता है और इसकी निर्भरताओं तक पहुँच सकता है।

5. रनटाइम बाइंडिंग

डैगर 2 में रनटाइम बाइंडिंग निर्भरताओं के प्रावधान को संदर्भित करता है, जो उस संदर्भ के आधार पर रनटाइम पर बनाए और प्रबंधित किए जाते हैं जिसमें उनकी आवश्यकता होती है।

  • अनुप्रयोग संदर्भ : उन निर्भरताओं के लिए उपयोग किया जाता है जिन्हें अनुप्रयोग के रूप में लंबे समय तक बने रहने की आवश्यकता होती है।
  • गतिविधि संदर्भ : उन निर्भरताओं के लिए उपयोग किया जाता है जिन्हें गतिविधि के समान लंबे समय तक बने रहने की आवश्यकता होती है।

1. आवेदन संदर्भ

परिभाषा : एप्लिकेशन संदर्भ एक ऐसा संदर्भ है जो पूरे एप्लिकेशन के जीवनचक्र से जुड़ा होता है। इसका उपयोग उन निर्भरताओं के लिए किया जाता है जिन्हें एप्लिकेशन के जितना ही समय तक जीवित रहना होता है।

निर्भरताएँ जो पूरे एप्लिकेशन में साझा की जाती हैं और प्रत्येक गतिविधि या खंड के लिए उन्हें फिर से बनाने की आवश्यकता नहीं होती है। उदाहरणों में नेटवर्क क्लाइंट, डेटाबेस इंस्टेंस और साझा प्राथमिकताएँ शामिल हैं।

उदाहरण :

 @Module public class AppModule { private final Application application; public AppModule(Application application) { this.application = application; } @Provides @Singleton Application provideApplication() { return application; } @Provides @Singleton Context provideApplicationContext() { return application.getApplicationContext(); } }

इस उदाहरण में, AppModule एक सिंगलटन निर्भरता के रूप में एप्लिकेशन संदर्भ प्रदान करता है। provideApplicationContext विधि यह सुनिश्चित करती है कि प्रदान किया गया संदर्भ एप्लिकेशन के जीवनचक्र से जुड़ा हुआ है।

2. गतिविधि संदर्भ

परिभाषा : गतिविधि संदर्भ एक संदर्भ है जो किसी विशिष्ट गतिविधि के जीवनचक्र से जुड़ा होता है। इसका उपयोग उन निर्भरताओं के लिए किया जाता है जिन्हें गतिविधि के समान ही लंबे समय तक जीवित रहने की आवश्यकता होती है।

निर्भरताएँ जो किसी गतिविधि के लिए विशिष्ट होती हैं और हर बार गतिविधि को फिर से बनाए जाने पर उन्हें फिर से बनाया जाना चाहिए। उदाहरणों में व्यू मॉडल, प्रेजेंटर और UI-संबंधित निर्भरताएँ शामिल हैं।

उदाहरण :

 @Module public class ActivityModule { private final Activity activity; public ActivityModule(Activity activity) { this.activity = activity; } @Provides @ActivityScope Activity provideActivity() { return activity; } @Provides @ActivityScope Context provideActivityContext() { return activity; } }

इस उदाहरण में, ActivityModule गतिविधि संदर्भ को एक स्कोप्ड निर्भरता के रूप में प्रदान करता है। provideActivityContext विधि यह सुनिश्चित करती है कि प्रदान किया गया संदर्भ गतिविधि के जीवनचक्र से जुड़ा हुआ है।

घटकों में रनटाइम बाइंडिंग का उपयोग करना

इन रनटाइम बाइंडिंग का उपयोग करने के लिए, आपको अपने घटकों में संबंधित मॉड्यूल शामिल करने होंगे:

आवेदन घटक :

 @Singleton @Component(modules = {AppModule.class, NetworkModule.class}) public interface ApplicationComponent { void inject(MyApplication application); Context getApplicationContext(); }

गतिविधि घटक :

 @ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); Context getActivityContext(); }

सन्दर्भों को शामिल करना

एक बार जब आप अपने घटकों और मॉड्यूलों को सेट कर लेते हैं, तो आप आवश्यकतानुसार अपने वर्गों में संदर्भों को सम्मिलित कर सकते हैं।

एक गतिविधि में उदाहरण :

 public class MainActivity extends AppCompatActivity { @Inject Context activityContext; @Inject Context applicationContext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ApplicationComponent appComponent = ((MyApplication) getApplication()).getApplicationComponent(); ActivityComponent activityComponent = DaggerActivityComponent.builder() .applicationComponent(appComponent) .activityModule(new ActivityModule(this)) .build(); activityComponent.inject(this); // Use the injected contexts Log.d("MainActivity", "Activity Context: " + activityContext); Log.d("MainActivity", "Application Context: " + applicationContext); } }

इस उदाहरण में, MainActivity निर्भरता इंजेक्शन के माध्यम से गतिविधि संदर्भ और अनुप्रयोग संदर्भ दोनों प्राप्त करता है। यह गतिविधि को निर्भरताओं की विशिष्ट आवश्यकताओं के आधार पर उपयुक्त संदर्भ का उपयोग करने की अनुमति देता है।

उदाहरण: एंड्रॉयड एप्लीकेशन में डैगर 2 का उपयोग करना

डैगर 2 की स्थापना

अपने प्रोजेक्ट में Dagger 2 का उपयोग करने के लिए, आपको अपनी build.gradle फ़ाइल में निम्नलिखित निर्भरताएँ जोड़नी होंगी:

 dependencies { implementation 'com.google.dagger:dagger:2.x' annotationProcessor 'com.google.dagger:dagger-compiler:2.x' }

2.x Dagger 2 के नवीनतम संस्करण से प्रतिस्थापित करें।

चरण 1: मॉड्यूल परिभाषित करें

निर्भरता प्रदान करने के लिए एक मॉड्यूल बनाएँ। उदाहरण के लिए, एक Retrofit इंस्टेंस प्रदान करने के लिए एक NetworkModule :

 @Module public class NetworkModule { @Provides @Singleton Retrofit provideRetrofit() { return new Retrofit.Builder() .baseUrl("https://api.example.com") .addConverterFactory(GsonConverterFactory.create()) .build(); } }

चरण 2: घटक परिभाषित करें

मॉड्यूल और उन कक्षाओं को जोड़ने के लिए एक घटक बनाएं जिन्हें निर्भरता की आवश्यकता है:

 @Singleton @Component(modules = {NetworkModule.class}) public interface ApplicationComponent { void inject(MyApplication application); }

चरण 3: निर्भरताएँ इंजेक्ट करें

अपने क्लास में निर्भरता को इंजेक्ट करने के लिए घटक का उपयोग करें। उदाहरण के लिए, अपने Application क्लास में:

 public class MyApplication extends Application { private ApplicationComponent applicationComponent; @Override public void onCreate() { super.onCreate(); applicationComponent = DaggerApplicationComponent.builder() .networkModule(new NetworkModule()) .build(); applicationComponent.inject(this); } public ApplicationComponent getApplicationComponent() { return applicationComponent; } }

चरण 4: इंजेक्टेड निर्भरता का उपयोग करें

अब आप अपनी कक्षाओं में इंजेक्ट की गई निर्भरता का उपयोग कर सकते हैं। उदाहरण के लिए, किसी Activity में:

 public class MainActivity extends AppCompatActivity { @Inject Retrofit retrofit; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ((MyApplication) getApplication()).getApplicationComponent().inject(this); // Use the injected Retrofit instance // ... } }

निष्कर्ष

आइये इस विषय को संक्षेप में प्रस्तुत करें:

  • DI का मुख्य उद्देश्य युग्मन को ढीला करना है, जिससे निर्भरताओं का प्रबंधन आसान हो जाता है।
  • DI का उपयोग करके, आप कोड का लचीलापन बढ़ा सकते हैं और परीक्षण प्रक्रिया को सरल बना सकते हैं।
  • DI एक जटिल विषय है जिसका परिदृश्य के आधार पर अलग-अलग कार्यान्वयन होता है।
  • विभिन्न भाषाओं में DI की अपनी विशिष्टताएं होती हैं जो आपके कार्य करने के तरीके को प्रभावित कर सकती हैं।
  • डैगर 2 निर्भरताओं को बनाने और प्रदान करने की प्रक्रिया को स्वचालित करता है, बॉयलरप्लेट कोड को कम करता है और रखरखाव में सुधार करता है।
  • डैगर 2 संकलन-समय सुरक्षा प्रदान करता है, तथा यह सुनिश्चित करता है कि अनुप्रयोग चलाने से पहले सभी निर्भरताएं संतुष्ट हो जाएं।
  • संकलन समय पर कोड उत्पन्न करके, डैगर 2 प्रतिबिंब-आधारित समाधानों से जुड़े प्रदर्शन ओवरहेड से बचता है।