9.1 निर्भरता उलटा
याद रखें, हमने एक बार कहा था कि सर्वर एप्लिकेशन में आप केवल स्ट्रीम नहीं बना सकते हैं new Thread().start()
? केवल कंटेनर को धागे बनाना चाहिए। अब हम इस विचार को और भी विकसित करेंगे।
सभी वस्तुओं को भी केवल कंटेनर द्वारा ही बनाया जाना चाहिए । बेशक, हम सभी वस्तुओं के बारे में बात नहीं कर रहे हैं, बल्कि तथाकथित व्यावसायिक वस्तुओं के बारे में बात कर रहे हैं। उन्हें अक्सर डिब्बे भी कहा जाता है। इस दृष्टिकोण के पैर SOLID के पांचवें सिद्धांत से बढ़ते हैं, जिसके लिए कक्षाओं से छुटकारा पाने और इंटरफेस में जाने की आवश्यकता होती है:
- शीर्ष स्तर के मॉड्यूल निचले स्तर के मॉड्यूल पर निर्भर नहीं होने चाहिए। वे दोनों, और अन्य को अमूर्तता पर निर्भर होना चाहिए।
- सार विवरण पर निर्भर नहीं होना चाहिए। कार्यान्वयन अमूर्तता पर निर्भर होना चाहिए।
मॉड्यूल में विशिष्ट कार्यान्वयन के संदर्भ नहीं होने चाहिए, और उनके बीच सभी निर्भरताएं और इंटरैक्शन पूरी तरह से सार (यानी, इंटरफेस) के आधार पर बनाए जाने चाहिए। इस नियम का सार एक वाक्यांश में लिखा जा सकता है: सभी निर्भरताएं इंटरफेस के रूप में होनी चाहिए ।
इसकी मौलिक प्रकृति और स्पष्ट सादगी के बावजूद, इस नियम का अक्सर उल्लंघन किया जाता है। अर्थात्, हर बार जब हम प्रोग्राम / मॉड्यूल के कोड में नए ऑपरेटर का उपयोग करते हैं और एक विशिष्ट प्रकार की एक नई वस्तु बनाते हैं, इस प्रकार, इंटरफ़ेस पर निर्भर होने के बजाय, कार्यान्वयन पर निर्भरता बनती है।
यह स्पष्ट है कि इससे बचा नहीं जा सकता है और वस्तुओं को कहीं बनाया जाना चाहिए। लेकिन, कम से कम, आपको उन स्थानों की संख्या को कम करने की आवश्यकता है जहां यह किया जाता है और जिसमें कक्षाएं स्पष्ट रूप से निर्दिष्ट हैं, साथ ही ऐसे स्थानों को स्थानीयकृत और अलग करना ताकि वे पूरे प्रोग्राम कोड में बिखरे न हों।
विशेष वस्तुओं और मॉड्यूल - कारखानों, सेवा लोकेटर, आईओसी कंटेनरों के भीतर नई वस्तुओं के निर्माण पर ध्यान केंद्रित करने का पागल विचार एक बहुत अच्छा समाधान है।
एक मायने में, ऐसा निर्णय एकल विकल्प सिद्धांत का पालन करता है, जो कहता है: "जब भी एक सॉफ्टवेयर सिस्टम को कई विकल्पों का समर्थन करना चाहिए, तो उनकी पूरी सूची सिस्टम के केवल एक मॉड्यूल के लिए जानी जानी चाहिए" ।
इसलिए, यदि भविष्य में नए विकल्प (या नए कार्यान्वयन, जैसा कि हम जिन नई वस्तुओं पर विचार कर रहे हैं) को जोड़ने के लिए आवश्यक है, तो यह केवल उस मॉड्यूल को अपडेट करने के लिए पर्याप्त होगा जिसमें यह जानकारी है, और अन्य सभी मॉड्यूल अप्रभावित रहेंगे और हमेशा की तरह अपना काम जारी रख सकेंगे।
उदाहरण 1
new ArrayList
ऐसा कुछ लिखने के बजाय, List.new()
JDK के लिए यह समझ में आता है कि वह आपको एक पत्ते का सही कार्यान्वयन प्रदान करे: ArrayList, LinkedList, या यहाँ तक कि समवर्ती सूची।
उदाहरण के लिए, कंपाइलर देखता है कि विभिन्न थ्रेड्स से ऑब्जेक्ट के लिए कॉल हैं और वहां थ्रेड-सुरक्षित कार्यान्वयन करता है। या शीट के बीच में बहुत अधिक आवेषण, तो कार्यान्वयन लिंक्डलिस्ट पर आधारित होगा।
उदाहरण 2
उदाहरण के लिए, यह पहले ही हो चुका है। आखिरी बार आपने संग्रह को सॉर्ट करने के लिए सॉर्टिंग एल्गोरिथम कब लिखा था? इसके बजाय, अब हर कोई विधि का उपयोग करता है Collections.sort()
, और संग्रह के तत्वों को तुलनीय इंटरफ़ेस (तुलनीय) का समर्थन करना चाहिए।
यदि sort()
आप विधि में 10 से कम तत्वों का संग्रह पास करते हैं, तो इसे बबल सॉर्ट (बबल सॉर्ट) के साथ सॉर्ट करना संभव है, न कि क्विकॉर्ट।
उदाहरण 3
कंपाइलर पहले से ही देख रहा है कि आप स्ट्रिंग्स को कैसे जोड़ते हैं और आपके कोड को StringBuilder.append()
.
9.2 व्यवहार में निर्भरता उलटा
अब सबसे दिलचस्प: आइए विचार करें कि हम सिद्धांत और व्यवहार को कैसे जोड़ सकते हैं। कैसे मॉड्यूल सही ढंग से अपनी "निर्भरता" बना सकते हैं और प्राप्त कर सकते हैं और निर्भरता व्युत्क्रम का उल्लंघन नहीं कर सकते हैं?
ऐसा करने के लिए, एक मॉड्यूल डिजाइन करते समय, आपको अपने लिए निर्णय लेना चाहिए:
- मॉड्यूल क्या करता है, यह क्या कार्य करता है;
- तब मॉड्यूल को अपने वातावरण से आवश्यकता होती है, अर्थात, उसे किन वस्तुओं / मॉड्यूलों से निपटना होगा;
- और वह कैसे मिलेगा?
निर्भरता व्युत्क्रम के सिद्धांतों का पालन करने के लिए, आपको निश्चित रूप से यह तय करने की आवश्यकता है कि आपका मॉड्यूल किन बाहरी वस्तुओं का उपयोग करता है और यह उनके संदर्भ कैसे प्राप्त करेगा।
और यहाँ निम्नलिखित विकल्प हैं:
- मॉड्यूल ही ऑब्जेक्ट बनाता है;
- मॉड्यूल कंटेनर से ऑब्जेक्ट लेता है;
- मॉड्यूल को पता नहीं है कि वस्तुएं कहां से आती हैं।
समस्या यह है कि एक वस्तु बनाने के लिए, आपको एक विशिष्ट प्रकार के निर्माता को कॉल करने की आवश्यकता होती है, और परिणामस्वरूप, मॉड्यूल इंटरफ़ेस पर नहीं, बल्कि विशिष्ट कार्यान्वयन पर निर्भर करेगा। लेकिन अगर हम मॉड्यूल कोड में वस्तुओं को स्पष्ट रूप से नहीं बनाना चाहते हैं, तो हम फ़ैक्टरी विधि पैटर्न का उपयोग कर सकते हैं ।
"लब्बोलुआब यह है कि नए के माध्यम से किसी ऑब्जेक्ट को सीधे इंस्टेंट करने के बजाय, हम क्लाइंट क्लास को ऑब्जेक्ट बनाने के लिए कुछ इंटरफ़ेस प्रदान करते हैं। चूंकि इस तरह के इंटरफ़ेस को हमेशा सही डिज़ाइन के साथ ओवरराइड किया जा सकता है, निम्न-स्तरीय मॉड्यूल का उपयोग करते समय हमें कुछ लचीलापन मिलता है उच्च स्तरीय मॉड्यूल में" ।
ऐसे मामलों में जहां संबंधित वस्तुओं के समूह या परिवार बनाना आवश्यक है, फैक्ट्री विधि के बजाय सार फैक्ट्री का उपयोग किया जाता है ।
9.3 सर्विस लोकेटर का उपयोग करना
मॉड्यूल आवश्यक वस्तुओं को उसी से लेता है जिसके पास पहले से है। यह माना जाता है कि सिस्टम में वस्तुओं का कुछ भंडार है, जिसमें मॉड्यूल अपनी वस्तुओं को "डाल" सकते हैं और वस्तुओं को भंडार से "ले" सकते हैं।
यह दृष्टिकोण सेवा लोकेटर पैटर्न द्वारा कार्यान्वित किया जाता है , जिसका मुख्य विचार यह है कि कार्यक्रम में एक वस्तु है जो जानता है कि सभी निर्भरताओं (सेवाओं) को कैसे प्राप्त किया जा सकता है।
कारखानों से मुख्य अंतर यह है कि सेवा लोकेटर वस्तुओं का निर्माण नहीं करता है, लेकिन वास्तव में पहले से ही तत्काल वस्तुओं को शामिल करता है (या जानता है कि उन्हें कहां/कैसे प्राप्त करना है, और यदि यह बनाता है, तो केवल पहली कॉल पर)। प्रत्येक कॉल पर फ़ैक्टरी एक नई वस्तु बनाती है जिसका आपको पूर्ण स्वामित्व प्राप्त होता है और आप इसके साथ जो चाहें कर सकते हैं।
महत्वपूर्ण ! सेवा लोकेटर पहले से ही मौजूद वस्तुओं के संदर्भ उत्पन्न करता है । इसलिए, आपको सर्विस लोकेटर द्वारा जारी वस्तुओं के साथ बहुत सावधान रहने की आवश्यकता है, क्योंकि कोई अन्य व्यक्ति उसी समय उनका उपयोग कर सकता है जब आप।
सेवा लोकेटर में वस्तुओं को सीधे कॉन्फ़िगरेशन फ़ाइल के माध्यम से जोड़ा जा सकता है, और वास्तव में किसी भी तरह से प्रोग्रामर के लिए सुविधाजनक है। सेवा लोकेटर स्वयं स्थैतिक विधियों, सिंगलटन, या इंटरफ़ेस के सेट के साथ एक स्थिर वर्ग हो सकता है, और इसे कन्स्ट्रक्टर या विधि के माध्यम से आवश्यक कक्षाओं में पारित किया जा सकता है।
सर्विस लोकेटर को कभी-कभी एंटी-पैटर्न कहा जाता है और इसे हतोत्साहित किया जाता है (क्योंकि यह निहित कनेक्शन बनाता है और केवल अच्छे डिजाइन का आभास देता है)। आप मार्क सीमैन से अधिक पढ़ सकते हैं:
9.4 निर्भरता इंजेक्शन
मॉड्यूल "खनन" निर्भरताओं के बारे में बिल्कुल परवाह नहीं करता है। यह केवल यह निर्धारित करता है कि इसे काम करने के लिए क्या चाहिए, और सभी आवश्यक निर्भरताओं को किसी और के द्वारा बाहर से आपूर्ति (शुरू) की जाती है।
इसे कहते हैं - डिपेंडेंसी इंजेक्शन। आम तौर पर, आवश्यक निर्भरताओं को या तो कन्स्ट्रक्टर पैरामीटर (कंस्ट्रक्टर इंजेक्शन) या कक्षा विधियों (सेटर इंजेक्शन) के माध्यम से पारित किया जाता है।
यह दृष्टिकोण निर्भरता बनाने की प्रक्रिया को उलट देता है - मॉड्यूल के बजाय, निर्भरता का निर्माण बाहर से किसी के द्वारा नियंत्रित किया जाता है। वस्तुओं के सक्रिय उत्सर्जक से मॉड्यूल निष्क्रिय हो जाता है - यह वह नहीं है जो बनाता है, बल्कि अन्य उसके लिए बनाते हैं।
दिशा में इस परिवर्तन को नियंत्रण का उलटा या हॉलीवुड सिद्धांत कहा जाता है - "हमें कॉल न करें, हम आपको कॉल करेंगे।"
यह सबसे लचीला समाधान है, जो मॉड्यूल को सबसे बड़ी स्वायत्तता देता है । हम कह सकते हैं कि केवल यह "एकल जिम्मेदारी सिद्धांत" को पूरी तरह से लागू करता है - मॉड्यूल को पूरी तरह से अपना काम अच्छी तरह से करने पर केंद्रित होना चाहिए और किसी और चीज की चिंता नहीं करनी चाहिए।
काम के लिए आवश्यक सब कुछ के साथ मॉड्यूल प्रदान करना एक अलग कार्य है, जिसे उपयुक्त "विशेषज्ञ" द्वारा नियंत्रित किया जाना चाहिए (आमतौर पर एक निश्चित कंटेनर, एक IoC कंटेनर, निर्भरता और उनके कार्यान्वयन के प्रबंधन के लिए जिम्मेदार होता है)।
वास्तव में, यहां सब कुछ जीवन की तरह है: एक सुव्यवस्थित कंपनी में, प्रोग्रामर प्रोग्राम, और डेस्क, कंप्यूटर और काम के लिए आवश्यक सभी चीजें कार्यालय प्रबंधक द्वारा खरीदी और प्रदान की जाती हैं। या, यदि आप एक कंस्ट्रक्टर के रूप में प्रोग्राम के रूपक का उपयोग करते हैं, तो मॉड्यूल को तारों के बारे में नहीं सोचना चाहिए, कोई अन्य व्यक्ति कंस्ट्रक्टर को असेंबल करने में शामिल होता है, न कि स्वयं भागों को।
यह कहना अतिशयोक्ति नहीं होगी कि मॉड्यूल (निर्भरता व्युत्क्रम) के बीच निर्भरता का वर्णन करने के लिए इंटरफेस का उपयोग + इन निर्भरताओं का सही निर्माण और इंजेक्शन (मुख्य रूप से निर्भरता इंजेक्शन) डिकॉउलिंग की प्रमुख तकनीकें हैं ।
वे नींव के रूप में कार्य करते हैं जिस पर कोड के ढीले युग्मन, इसकी लचीलापन, परिवर्तन के प्रतिरोध, पुन: उपयोग, और जिसके बिना अन्य सभी तकनीकों का कोई मतलब नहीं है। यह ढीले युग्मन और अच्छी वास्तुकला की नींव है।
मार्टिन फाउलर द्वारा नियंत्रण के व्युत्क्रम (डिपेंडेंसी इंजेक्शन और सर्विस लोकेटर के साथ) के सिद्धांत पर विस्तार से चर्चा की गई है। उनके दोनों लेखों के अनुवाद हैं: "नियंत्रण कंटेनरों का उलटा और निर्भरता इंजेक्शन पैटर्न" और "नियंत्रण का उलटा" ।