सामान्य मॉड्यूलराइज़ेशन पैटर्न

मॉड्यूलरिटी की कोई ऐसी रणनीति नहीं है जो सभी प्रोजेक्ट के लिए सही हो. Gradle के फ़्लेक्सिबल नेचर की वजह से, प्रोजेक्ट को व्यवस्थित करने के तरीके पर कुछ पाबंदियां हैं. इस पेज पर, कुछ सामान्य नियमों और सामान्य पैटर्न की खास जानकारी दी गई है. इनका इस्तेमाल, एक से ज़्यादा मॉड्यूल वाले Android ऐप्लिकेशन डेवलप करते समय किया जा सकता है.

हाई कोहेशन और लो कपलिंग का सिद्धांत

मॉड्यूलर कोडबेस की विशेषता बताने के लिए, कपलिंग और कोहेशन प्रॉपर्टी का इस्तेमाल किया जा सकता है. कपलिंग से यह पता चलता है कि मॉड्यूल एक-दूसरे पर किस हद तक निर्भर हैं. इस संदर्भ में कोहेशन से यह पता चलता है कि किसी एक मॉड्यूल के एलिमेंट, फ़ंक्शन के हिसाब से कैसे जुड़े हुए हैं. सामान्य नियम के तौर पर, आपको कम कपलिंग और ज़्यादा कोहेशन का इस्तेमाल करना चाहिए:

  • कम कपलिंग का मतलब है कि मॉड्यूल को एक-दूसरे से जितना हो सके उतना स्वतंत्र होना चाहिए, ताकि एक मॉड्यूल में किए गए बदलावों का असर दूसरे मॉड्यूल पर न पड़े या कम से कम पड़े. मॉड्यूल को अन्य मॉड्यूल के काम करने के तरीके के बारे में जानकारी नहीं होनी चाहिए.
  • हाई कोहेशन का मतलब है कि मॉड्यूल में कोड का ऐसा कलेक्शन होना चाहिए जो सिस्टम के तौर पर काम करे. उनकी ज़िम्मेदारियां साफ़ तौर पर तय होनी चाहिए. साथ ही, उन्हें किसी खास डोमेन की जानकारी की सीमाओं के अंदर काम करना चाहिए. ई-बुक ऐप्लिकेशन का सैंपल देखें. किताब और पेमेंट से जुड़े कोड को एक ही मॉड्यूल में मिक्स करना सही नहीं है, क्योंकि ये दो अलग-अलग फ़ंक्शनल डोमेन हैं.

मॉड्यूल के टाइप

मॉड्यूल को व्यवस्थित करने का तरीका, मुख्य रूप से आपके ऐप्लिकेशन के आर्किटेक्चर पर निर्भर करता है. यहां कुछ सामान्य तरह के मॉड्यूल दिए गए हैं. इन्हें सुझाए गए ऐप्लिकेशन आर्किटेक्चर के मुताबिक, अपने ऐप्लिकेशन में शामिल किया जा सकता है.

डेटा मॉड्यूल

डेटा मॉड्यूल में आम तौर पर, रिपॉज़िटरी, डेटा सोर्स, और मॉडल क्लास शामिल होती हैं. डेटा मॉड्यूल की तीन मुख्य ज़िम्मेदारियां ये हैं:

  1. किसी डोमेन के सभी डेटा और कारोबार के लॉजिक को शामिल करना: हर डेटा मॉड्यूल को ऐसे डेटा को मैनेज करने की ज़िम्मेदारी लेनी चाहिए जो किसी डोमेन को दिखाता हो. यह कई तरह के डेटा को प्रोसेस कर सकता है. हालांकि, यह ज़रूरी है कि डेटा एक-दूसरे से जुड़ा हो.
  2. रिपॉज़िटरी को बाहरी एपीआई के तौर पर उपलब्ध कराएं: डेटा मॉड्यूल का सार्वजनिक एपीआई, रिपॉज़िटरी होना चाहिए. ऐसा इसलिए, क्योंकि ये रिपॉज़िटरी ही ऐप्लिकेशन के बाकी हिस्सों को डेटा उपलब्ध कराती हैं.
  3. बाहरी लोगों से, लागू करने से जुड़ी सभी जानकारी और डेटा सोर्स छिपाएं: डेटा सोर्स को सिर्फ़ एक ही मॉड्यूल की रिपॉज़िटरी ऐक्सेस कर सकती हैं. ये बाहर के लोगों को नहीं दिखते. Kotlin के private या internal विज़िबिलिटी कीवर्ड का इस्तेमाल करके, इसे लागू किया जा सकता है.
पहली इमेज. सैंपल डेटा मॉड्यूल और उनका कॉन्टेंट.

फ़ीचर मॉड्यूल

सुविधा, ऐप्लिकेशन की किसी सुविधा का अलग हिस्सा होती है. यह आम तौर पर किसी स्क्रीन या उससे मिलती-जुलती स्क्रीन की सीरीज़ से जुड़ी होती है. जैसे, साइन अप या चेकआउट फ़्लो. अगर आपके ऐप्लिकेशन में बॉटम बार नेविगेशन है, तो हो सकता है कि हर डेस्टिनेशन एक सुविधा हो.

दूसरी इमेज. इस ऐप्लिकेशन के हर टैब को एक सुविधा के तौर पर तय किया जा सकता है.

सुविधाएं, आपके ऐप्लिकेशन में मौजूद स्क्रीन या डेस्टिनेशन से जुड़ी होती हैं. इसलिए, इनमें यूज़र इंटरफ़ेस (यूआई) और ViewModel होता है, ताकि इनके लॉजिक और स्थिति को मैनेज किया जा सके. किसी एक सुविधा को सिर्फ़ एक व्यू या नेविगेशन डेस्टिनेशन तक सीमित नहीं रखा जाना चाहिए. फ़ीचर मॉड्यूल, डेटा मॉड्यूल पर निर्भर करते हैं.

तीसरी इमेज. फ़ीचर मॉड्यूल और उनके कॉन्टेंट का सैंपल.

ऐप्लिकेशन मॉड्यूल

ऐप्लिकेशन मॉड्यूल, ऐप्लिकेशन में एंट्री करने का पॉइंट होते हैं. ये सुविधा मॉड्यूल पर निर्भर होते हैं और आम तौर पर, रूट नेविगेशन की सुविधा देते हैं. बिल्ड वैरिएंट की मदद से, किसी एक ऐप्लिकेशन मॉड्यूल को कई अलग-अलग बाइनरी में कंपाइल किया जा सकता है.

चौथी इमेज. *Demo* और *Full* प्रॉडक्ट फ़्लेवर मॉड्यूल का डिपेंडेंसी ग्राफ़.

अगर आपका ऐप्लिकेशन Android Auto, Wear या TV जैसे कई तरह के डिवाइसों के लिए बनाया गया है, तो हर डिवाइस के लिए एक ऐप्लिकेशन मॉड्यूल तय करें. इससे प्लैटफ़ॉर्म के हिसाब से तय की गई डिपेंडेंसी को अलग करने में मदद मिलती है.

पांचवीं इमेज. Android Auto ऐप्लिकेशन का डिपेंडेंसी ग्राफ़.

सामान्य मॉड्यूल

कॉमन मॉड्यूल को कोर मॉड्यूल भी कहा जाता है. इनमें ऐसा कोड होता है जिसका इस्तेमाल अन्य मॉड्यूल अक्सर करते हैं. ये कोड को दोहराने से रोकते हैं और ऐप्लिकेशन के आर्किटेक्चर में किसी खास लेयर को नहीं दिखाते. यहां सामान्य मॉड्यूल के उदाहरण दिए गए हैं:

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

टेस्ट मॉड्यूल

टेस्ट मॉड्यूल, Android मॉड्यूल होते हैं. इनका इस्तेमाल सिर्फ़ टेस्टिंग के लिए किया जाता है. मॉड्यूल में टेस्ट कोड, टेस्ट संसाधन, और टेस्ट डिपेंडेंसी शामिल होती हैं. इनकी ज़रूरत सिर्फ़ टेस्ट चलाने के लिए होती है. ऐप्लिकेशन के रनटाइम के दौरान इनकी ज़रूरत नहीं होती. टेस्ट मॉड्यूल, टेस्ट से जुड़े कोड को मुख्य ऐप्लिकेशन से अलग करने के लिए बनाए जाते हैं. इससे मॉड्यूल कोड को मैनेज करना और बनाए रखना आसान हो जाता है.

टेस्ट मॉड्यूल के इस्तेमाल के उदाहरण

यहां कुछ उदाहरण दिए गए हैं, जिनमें टेस्ट मॉड्यूल लागू करना फ़ायदेमंद हो सकता है:

  • शेयर किया गया टेस्ट कोड: अगर आपके प्रोजेक्ट में कई मॉड्यूल हैं और कुछ टेस्ट कोड एक से ज़्यादा मॉड्यूल पर लागू होते हैं, तो कोड शेयर करने के लिए एक टेस्ट मॉड्यूल बनाया जा सकता है. इससे डुप्लीकेट कोड को कम करने और टेस्ट कोड को बनाए रखने में आसानी होती है. शेयर किए गए टेस्ट कोड में, यूटिलिटी क्लास या फ़ंक्शन शामिल हो सकते हैं. जैसे, कस्टम असर्शन या मैच करने वाले फ़ंक्शन. साथ ही, इसमें टेस्ट डेटा भी शामिल हो सकता है. जैसे, सिम्युलेट किए गए JSON जवाब.

  • बेहतर बिल्ड कॉन्फ़िगरेशन: टेस्ट मॉड्यूल की मदद से, बेहतर बिल्ड कॉन्फ़िगरेशन बनाए जा सकते हैं. ऐसा इसलिए, क्योंकि इनके पास अपनी build.gradle फ़ाइल हो सकती है. आपको अपने ऐप्लिकेशन मॉड्यूल की build.gradle फ़ाइल में, ऐसे कॉन्फ़िगरेशन शामिल करने की ज़रूरत नहीं है जो सिर्फ़ टेस्ट के लिए ज़रूरी हैं.

  • इंटिग्रेशन टेस्ट: टेस्ट मॉड्यूल का इस्तेमाल, इंटिग्रेशन टेस्ट को सेव करने के लिए किया जा सकता है. इनका इस्तेमाल, आपके ऐप्लिकेशन के अलग-अलग हिस्सों के बीच इंटरैक्शन की जांच करने के लिए किया जाता है. इनमें यूज़र इंटरफ़ेस, कारोबार की लॉजिक, नेटवर्क अनुरोध, और डेटाबेस क्वेरी शामिल हैं.

  • बड़े पैमाने पर इस्तेमाल किए जाने वाले ऐप्लिकेशन: टेस्ट मॉड्यूल, खास तौर पर बड़े पैमाने पर इस्तेमाल किए जाने वाले उन ऐप्लिकेशन के लिए फ़ायदेमंद होते हैं जिनमें जटिल कोडबेस और कई मॉड्यूल होते हैं. ऐसे मामलों में, टेस्ट मॉड्यूल से कोड को व्यवस्थित करने और उसे बनाए रखने में मदद मिल सकती है.

छठी इमेज. टेस्ट मॉड्यूल का इस्तेमाल, उन मॉड्यूल को अलग करने के लिए किया जा सकता है जो एक-दूसरे पर निर्भर होते हैं.

मॉड्यूल से मॉड्यूल के बीच कम्यूनिकेशन

मॉड्यूल, पूरी तरह से अलग-अलग नहीं होते. वे अक्सर एक-दूसरे पर निर्भर होते हैं और एक-दूसरे के साथ कम्यूनिकेट करते हैं. मॉड्यूल के एक साथ काम करने और बार-बार जानकारी शेयर करने पर भी, कपलिंग को कम रखना ज़रूरी है. कभी-कभी दो मॉड्यूल के बीच सीधे तौर पर कम्यूनिकेशन करना सही नहीं होता. जैसे, आर्किटेक्चर की सीमाओं के मामले में. ऐसा भी हो सकता है कि यह मुमकिन न हो. जैसे, साइक्लिक डिपेंडेंसी के मामले में.

सातवीं इमेज. साइक्लिक डिपेंडेंसी की वजह से, मॉड्यूल के बीच सीधे तौर पर दोनों तरफ़ से कम्यूनिकेशन नहीं किया जा सकता. दो अन्य स्वतंत्र मॉड्यूल के बीच डेटा के लेन-देन को मैनेज करने के लिए, मीडिएटिंग मॉड्यूल ज़रूरी है.

इस समस्या को हल करने के लिए, दो अन्य मॉड्यूल के बीच तीसरे मॉड्यूल को मध्यस्थ के तौर पर इस्तेमाल किया जा सकता है. मीडिएटर मॉड्यूल, दोनों मॉड्यूल से मिले मैसेज को सुन सकता है और उन्हें ज़रूरत के हिसाब से फ़ॉरवर्ड कर सकता है. हमारे सैंपल ऐप्लिकेशन में, चेकआउट स्क्रीन को यह पता होना चाहिए कि कौनसी किताब खरीदनी है. भले ही, इवेंट किसी दूसरी स्क्रीन से शुरू हुआ हो, जो किसी दूसरी सुविधा का हिस्सा है. इस मामले में, मीडिएटर वह मॉड्यूल होता है जिसके पास नेविगेशन ग्राफ़ का मालिकाना हक होता है. आम तौर पर, यह ऐप्लिकेशन मॉड्यूल होता है. इस उदाहरण में, Navigation कॉम्पोनेंट का इस्तेमाल करके, होम फ़ीचर से चेकआउट फ़ीचर तक डेटा पास करने के लिए नेविगेशन का इस्तेमाल किया गया है.

navController.navigate("checkout/$bookId")

चेकआउट डेस्टिनेशन को एक आर्ग्युमेंट के तौर पर किताब का आईडी मिलता है. इसका इस्तेमाल करके, वह किताब के बारे में जानकारी फ़ेच करता है. सेव किए गए स्टेट हैंडल का इस्तेमाल करके, डेस्टिनेशन फ़ीचर के ViewModel में नेविगेशन आर्ग्युमेंट वापस पाए जा सकते हैं.

class CheckoutViewModel(savedStateHandle: SavedStateHandle, ) : ViewModel() {

   val uiState: StateFlow<CheckoutUiState> =
      savedStateHandle.getStateFlow<String>("bookId", "").map { bookId ->
          // produce UI state calling bookRepository.getBook(bookId)
      }
      
}

आपको नेविगेशन के तर्कों के तौर पर ऑब्जेक्ट पास नहीं करने चाहिए. इसके बजाय, ऐसे सामान्य आईडी का इस्तेमाल करें जिनका इस्तेमाल करके सुविधाएं, डेटा लेयर से ज़रूरी संसाधनों को ऐक्सेस और लोड कर सकती हैं. इस तरह, कपलिंग कम रहती है और सिंगल सोर्स ऑफ़ ट्रुथ सिद्धांत का उल्लंघन नहीं होता.

यहां दिए गए उदाहरण में, दोनों फ़ीचर मॉड्यूल एक ही डेटा मॉड्यूल पर निर्भर हैं. इससे मीडिएटर मॉड्यूल को फ़ॉरवर्ड करने के लिए ज़रूरी डेटा की मात्रा को कम किया जा सकता है. साथ ही, इससे मॉड्यूल के बीच कपलिंग कम रहती है. ऑब्जेक्ट पास करने के बजाय, मॉड्यूल को प्रिमिटिव आईडी का आदान-प्रदान करना चाहिए. साथ ही, शेयर किए गए डेटा मॉड्यूल से संसाधनों को लोड करना चाहिए.

आठवीं इमेज. शेयर किए गए डेटा मॉड्यूल पर निर्भर दो फ़ीचर मॉड्यूल.

डिपेंडेंसी इनवर्ज़न

डिपेंडेंसी इनवर्ज़न का मतलब है कि अपने कोड को इस तरह से व्यवस्थित करना कि ऐब्स्ट्रैक्शन, कॉन्क्रीट इंप्लीमेंटेशन से अलग हो.

  • ऐब्स्ट्रैक्शन: यह एक ऐसा कॉन्ट्रैक्ट होता है जो यह तय करता है कि आपके ऐप्लिकेशन में मौजूद कॉम्पोनेंट या मॉड्यूल एक-दूसरे के साथ कैसे इंटरैक्ट करेंगे. ऐब्स्ट्रैक्शन मॉड्यूल, आपके सिस्टम के एपीआई के बारे में बताते हैं. इनमें इंटरफ़ेस और मॉडल शामिल होते हैं.
  • कॉन्क्रीट इंप्लीमेंटेशन: ऐसे मॉड्यूल जो ऐब्स्ट्रैक्शन मॉड्यूल पर निर्भर होते हैं और ऐब्स्ट्रैक्शन के व्यवहार को लागू करते हैं.

जिन मॉड्यूल के लिए, ऐब्स्ट्रैक्शन मॉड्यूल में तय किए गए व्यवहार पर भरोसा करना होता है उन्हें सिर्फ़ ऐब्स्ट्रैक्शन पर निर्भर रहना चाहिए. उन्हें किसी खास तरीके से लागू करने पर निर्भर नहीं रहना चाहिए.

नौवीं इमेज. ज़्यादा अहमियत वाले मॉड्यूल, कम अहमियत वाले मॉड्यूल पर सीधे तौर पर निर्भर रहने के बजाय, ऐब्स्ट्रैक्शन मॉड्यूल पर निर्भर रहते हैं.

उदाहरण

मान लें कि किसी फ़ीचर मॉड्यूल को काम करने के लिए डेटाबेस की ज़रूरत है. फ़ीचर मॉड्यूल को इस बात से कोई फ़र्क़ नहीं पड़ता कि डेटाबेस को कैसे लागू किया गया है. यह लोकल रूम डेटाबेस या रिमोट फ़ायरस्टोर इंस्टेंस हो सकता है. इसे सिर्फ़ ऐप्लिकेशन के डेटा को सेव करने और पढ़ने की ज़रूरत होती है.

ऐसा करने के लिए, फ़ीचर मॉड्यूल किसी खास डेटाबेस के बजाय, ऐब्स्ट्रैक्शन मॉड्यूल पर निर्भर करता है. यह ऐब्स्ट्रैक्शन, ऐप्लिकेशन के डेटाबेस एपीआई को तय करता है. दूसरे शब्दों में कहें, तो यह डेटाबेस के साथ इंटरैक्ट करने के नियम सेट करता है. इससे फ़ीचर मॉड्यूल, किसी भी डेटाबेस का इस्तेमाल कर सकता है. इसके लिए, उसे डेटाबेस को लागू करने से जुड़ी बुनियादी जानकारी की ज़रूरत नहीं होती.

कॉन्क्रीट इंप्लीमेंटेशन मॉड्यूल, ऐब्स्ट्रैक्शन मॉड्यूल में तय किए गए एपीआई को लागू करता है. ऐसा करने के लिए, लागू करने वाला मॉड्यूल, ऐब्स्ट्रैक्शन मॉड्यूल पर भी निर्भर करता है.

डिपेंडेंसी इंजेक्शन

अब आपके मन में यह सवाल उठ रहा होगा कि फ़ीचर मॉड्यूल, इंप्लीमेंटेशन मॉड्यूल से कैसे कनेक्ट होता है. इसका जवाब डिपेंडेंसी इंजेक्शन है. यह सुविधा मॉड्यूल, सीधे तौर पर ज़रूरी डेटाबेस इंस्टेंस नहीं बनाता है. इसके बजाय, यह बताता है कि इसे किन डिपेंडेंसी की ज़रूरत है. इसके बाद, इन डिपेंडेंसी को बाहरी तौर पर उपलब्ध कराया जाता है. आम तौर पर, ऐसा ऐप्लिकेशन मॉड्यूल में किया जाता है.

releaseImplementation(project(":database:impl:firestore"))

debugImplementation(project(":database:impl:room"))

androidTestImplementation(project(":database:impl:mock"))

फ़ायदे

एपीआई और उनके लागू करने के तरीके को अलग-अलग करने के ये फ़ायदे हैं:

  • बदलाव की सुविधा: एपीआई और लागू करने से जुड़े मॉड्यूल को अलग-अलग करने से, एक ही एपीआई के लिए कई तरीके से लागू करने की सुविधा डेवलप की जा सकती है. साथ ही, एपीआई का इस्तेमाल करने वाले कोड में बदलाव किए बिना, एक से दूसरे तरीके पर स्विच किया जा सकता है. यह उन स्थितियों में खास तौर पर फ़ायदेमंद हो सकता है जहां आपको अलग-अलग संदर्भों में अलग-अलग सुविधाएं या व्यवहार उपलब्ध कराने हों. उदाहरण के लिए, टेस्टिंग के लिए मॉक इंपलीमेंटेशन बनाम प्रोडक्शन के लिए असली इंप्लीमेंटेशन.
  • डिकपल करना: इसका मतलब है कि ऐब्स्ट्रैक्शन का इस्तेमाल करने वाले मॉड्यूल, किसी खास टेक्नोलॉजी पर निर्भर नहीं होते. अगर आपको बाद में अपने डेटाबेस को Room से Firestore में बदलना है, तो यह काम आसानी से हो जाएगा. ऐसा इसलिए, क्योंकि बदलाव सिर्फ़ उस मॉड्यूल में होंगे जो डेटाबेस को लागू करता है. साथ ही, इससे डेटाबेस के एपीआई का इस्तेमाल करने वाले अन्य मॉड्यूल पर कोई असर नहीं पड़ेगा.
  • टेस्ट करने की सुविधा: एपीआई को उनके लागू करने के तरीके से अलग करने पर, टेस्ट करना बहुत आसान हो जाता है. एपीआई अनुबंधों के ख़िलाफ़ टेस्ट केस लिखे जा सकते हैं. अलग-अलग स्थितियों और मुश्किल मामलों की जांच करने के लिए, अलग-अलग तरीके से लागू करने की सुविधा का इस्तेमाल किया जा सकता है. इसमें मॉक इंप्लीमेंटेशन भी शामिल हैं.
  • बेहतर बिल्ड परफ़ॉर्मेंस: जब किसी एपीआई और उसके लागू करने के तरीके को अलग-अलग मॉड्यूल में बांटा जाता है, तो लागू करने के तरीके वाले मॉड्यूल में किए गए बदलावों की वजह से, बिल्ड सिस्टम को एपीआई मॉड्यूल पर निर्भर मॉड्यूल को फिर से कंपाइल करने के लिए मजबूर नहीं होना पड़ता. इससे बिल्ड करने में कम समय लगता है और प्रॉडक्टिविटी बढ़ती है. खास तौर पर, बड़े प्रोजेक्ट में ऐसा होता है, जहां बिल्ड करने में ज़्यादा समय लग सकता है.

कब अलग करना है

इन मामलों में, अपने एपीआई को उनके लागू करने के तरीके से अलग रखना फ़ायदेमंद होता है:

  • अलग-अलग सुविधाएं: अगर आपके सिस्टम के कुछ हिस्सों को कई तरीकों से लागू किया जा सकता है, तो साफ़ तौर पर बताया गया एपीआई, अलग-अलग तरीकों से लागू करने की सुविधा देता है. उदाहरण के लिए, आपके पास ऐसा रेंडरिंग सिस्टम हो सकता है जो OpenGL या Vulkan का इस्तेमाल करता हो. इसके अलावा, ऐसा बिलिंग सिस्टम भी हो सकता है जो Play या आपके इन-हाउस बिलिंग एपीआई के साथ काम करता हो.
  • एक से ज़्यादा ऐप्लिकेशन: अगर आपको अलग-अलग प्लैटफ़ॉर्म के लिए, शेयर की गई सुविधाओं वाले एक से ज़्यादा ऐप्लिकेशन डेवलप करने हैं, तो सामान्य एपीआई तय किए जा सकते हैं. साथ ही, हर प्लैटफ़ॉर्म के लिए खास तौर पर लागू होने वाली सुविधाएं डेवलप की जा सकती हैं.
  • इंडिपेंडेंट टीमें: अलग-अलग डेवलपर या टीमें, कोडबेस के अलग-अलग हिस्सों पर एक साथ काम कर सकती हैं. डेवलपर को एपीआई कॉन्ट्रैक्ट को समझने और उनका सही तरीके से इस्तेमाल करने पर ध्यान देना चाहिए. उन्हें अन्य मॉड्यूल को लागू करने के बारे में चिंता करने की ज़रूरत नहीं है.
  • बड़ा कोडबेस: जब कोडबेस बड़ा या जटिल होता है, तो एपीआई को लागू करने से अलग करने पर कोड को मैनेज करना आसान हो जाता है. इससे कोडबेस को ज़्यादा बारीकी से समझने, मैनेज करने, और उसे छोटे-छोटे हिस्सों में बांटने में मदद मिलती है.

इसे कैसे लागू करें?

डिपेंडेंसी इनवर्ज़न लागू करने के लिए, यह तरीका अपनाएं:

  1. ऐब्स्ट्रैक्शन मॉड्यूल बनाएं: इस मॉड्यूल में ऐसे एपीआई (इंटरफ़ेस और मॉडल) होने चाहिए जो आपकी सुविधा के काम करने के तरीके के बारे में बताते हों.
  2. लागू करने के मॉड्यूल बनाएं: लागू करने के मॉड्यूल, एपीआई मॉड्यूल पर निर्भर होने चाहिए. साथ ही, उन्हें ऐब्स्ट्रैक्शन के व्यवहार को लागू करना चाहिए.
    ज़्यादा लेवल के मॉड्यूल, सीधे तौर पर कम लेवल के मॉड्यूल पर निर्भर होने के बजाय, ऐब्स्ट्रैक्शन मॉड्यूल पर निर्भर होते हैं. साथ ही, लागू करने वाले मॉड्यूल भी ऐब्स्ट्रैक्शन मॉड्यूल पर निर्भर होते हैं.
    दसवीं इमेज. लागू करने वाले मॉड्यूल, ऐब्स्ट्रैक्शन मॉड्यूल पर निर्भर करते हैं.
  3. हाई लेवल मॉड्यूल को ऐब्स्ट्रैक्शन मॉड्यूल पर निर्भर बनाएं: किसी खास तरीके को सीधे तौर पर इस्तेमाल करने के बजाय, अपने मॉड्यूल को ऐब्स्ट्रैक्शन मॉड्यूल पर निर्भर बनाएं. हाई लेवल मॉड्यूल को लागू करने से जुड़ी जानकारी की ज़रूरत नहीं होती. उन्हें सिर्फ़ अनुबंध (एपीआई) की ज़रूरत होती है.
    हाई लेवल मॉड्यूल, ऐब्स्ट्रैक्शन पर निर्भर करते हैं, न कि लागू करने पर.
    ग्यारहवीं इमेज. हाई लेवल मॉड्यूल, ऐब्स्ट्रैक्शन पर निर्भर करते हैं, न कि लागू करने पर.
  4. लागू करने से जुड़ा मॉड्यूल उपलब्ध कराएं: आखिर में, आपको अपनी डिपेंडेंसी के लिए, लागू करने से जुड़ा मॉड्यूल उपलब्ध कराना होगा. लागू करने का तरीका, आपके प्रोजेक्ट के सेटअप पर निर्भर करता है. हालांकि, आम तौर पर ऐप्लिकेशन मॉड्यूल में ऐसा करना सही रहता है. लागू करने के लिए, इसे चुने गए बिल्ड वैरिएंट या टेस्टिंग सोर्स सेट के लिए डिपेंडेंसी के तौर पर तय करें.
    ऐप्लिकेशन मॉड्यूल, असल में लागू करने की सुविधा देता है.
    इमेज 12. ऐप्लिकेशन मॉड्यूल, असल में लागू करने की सुविधा देता है.

सबसे सही तरीके

जैसा कि शुरुआत में बताया गया है, मल्टी-मॉड्यूल ऐप्लिकेशन डेवलप करने का कोई एक सही तरीका नहीं है. जिस तरह कई सॉफ़्टवेयर आर्किटेक्चर होते हैं उसी तरह किसी ऐप्लिकेशन को मॉड्यूलर बनाने के कई तरीके होते हैं. हालाँकि, यहाँ दी गई सामान्य सलाह से आपको अपने कोड को ज़्यादा आसानी से समझने, बनाए रखने, और टेस्ट करने में मदद मिल सकती है.

अपने कॉन्फ़िगरेशन को एक जैसा रखें

हर मॉड्यूल में कॉन्फ़िगरेशन ओवरहेड होता है. अगर आपके मॉड्यूल की संख्या एक तय थ्रेशोल्ड तक पहुंच जाती है, तो एक जैसा कॉन्फ़िगरेशन मैनेज करना मुश्किल हो जाता है. उदाहरण के लिए, यह ज़रूरी है कि मॉड्यूल एक ही वर्शन की डिपेंडेंसी का इस्तेमाल करें. अगर आपको किसी डिपेंडेंसी के वर्शन को अपडेट करने के लिए, कई मॉड्यूल अपडेट करने पड़ते हैं, तो इसमें न सिर्फ़ ज़्यादा समय लगता है, बल्कि गलतियां होने की भी आशंका रहती है. इस समस्या को हल करने के लिए, Gradle के किसी टूल का इस्तेमाल करके कॉन्फ़िगरेशन को एक जगह पर रखा जा सकता है:

  • वर्शन कैटलॉग, डिपेंडेंसी की टाइप सेफ़ सूची होती है. इसे सिंक करने के दौरान Gradle जनरेट करता है. यह एक ऐसी जगह है जहां सभी डिपेंडेंसी का एलान किया जाता है. साथ ही, यह किसी प्रोजेक्ट के सभी मॉड्यूल के लिए उपलब्ध होती है.
  • मॉड्यूल के बीच बिल्ड लॉजिक शेयर करने के लिए, कन्वेंशन प्लगिन का इस्तेमाल करें.

कम से कम जानकारी शेयर करें

किसी मॉड्यूल का सार्वजनिक इंटरफ़ेस कम से कम होना चाहिए और उसमें सिर्फ़ ज़रूरी चीज़ें दिखनी चाहिए. इससे लागू करने से जुड़ी कोई भी जानकारी बाहर नहीं जानी चाहिए. स्कोप को जितना हो सके उतना कम करें. डिक्लेरेशन को मॉड्यूल-प्राइवेट बनाने के लिए, Kotlin के private या internal विज़िबिलिटी स्कोप का इस्तेमाल करें. अपने मॉड्यूल में डिपेंडेंसी का एलान करते समय, api के बजाय implementation का इस्तेमाल करें. बाद वाला विकल्प, आपके मॉड्यूल के उपभोक्ताओं को ट्रांज़िटिव डिपेंडेंसी दिखाता है. इस्तेमाल करने से, बिल्ड होने में लगने वाला समय कम हो सकता है. ऐसा इसलिए, क्योंकि इससे उन मॉड्यूल की संख्या कम हो जाती है जिन्हें फिर से बनाना होता है.

Kotlin और Java मॉड्यूल को प्राथमिकता दें

Android Studio में तीन तरह के मॉड्यूल इस्तेमाल किए जा सकते हैं:

  • ऐप्लिकेशन मॉड्यूल, आपके ऐप्लिकेशन का एंट्री पॉइंट होते हैं. इनमें सोर्स कोड, संसाधन, ऐसेट, और AndroidManifest.xml शामिल हो सकते हैं. ऐप्लिकेशन मॉड्यूल का आउटपुट, Android ऐप्लिकेशन बंडल (AAB) या Android ऐप्लिकेशन पैकेज (APK) होता है.
  • लाइब्रेरी मॉड्यूल में वही कॉन्टेंट होता है जो ऐप्लिकेशन मॉड्यूल में होता है. इनका इस्तेमाल, अन्य Android मॉड्यूल में डिपेंडेंसी के तौर पर किया जाता है. लाइब्रेरी मॉड्यूल का आउटपुट, Android Archive (AAR) होता है. यह ऐप्लिकेशन मॉड्यूल के जैसा ही होता है. हालांकि, इसे Android Archive (AAR) फ़ाइल में कंपाइल किया जाता है. बाद में, अन्य मॉड्यूल इसका इस्तेमाल डिपेंडेंसी के तौर पर कर सकते हैं. लाइब्रेरी मॉड्यूल की मदद से, एक ही लॉजिक और संसाधनों को कई ऐप्लिकेशन मॉड्यूल में इस्तेमाल किया जा सकता है.
  • Kotlin और Java लाइब्रेरी में कोई Android संसाधन, ऐसेट या मेनिफ़ेस्ट फ़ाइलें शामिल नहीं होती हैं.

Android मॉड्यूल में ओवरहेड होता है. इसलिए, हमारा सुझाव है कि आप ज़्यादा से ज़्यादा Kotlin या Java का इस्तेमाल करें.