निर्भरता व्युत्क्रम सिद्धांत क्या है और यह महत्वपूर्ण क्यों है?
निर्भरता व्युत्क्रम सिद्धांत क्या है और यह महत्वपूर्ण क्यों है?
जवाबों:
इस दस्तावेज़ को देखें: डिपेंडेंसी इन्वर्सेशन प्रिंसिपल ।
यह मूल रूप से कहता है:
जैसा कि यह महत्वपूर्ण क्यों है, संक्षेप में: परिवर्तन जोखिम भरा है, और कार्यान्वयन के बजाय एक अवधारणा के आधार पर, आप कॉल साइटों पर परिवर्तन की आवश्यकता को कम करते हैं।
प्रभावी रूप से, DIP कोड के विभिन्न टुकड़ों के बीच युग्मन को कम करता है। विचार यह है कि यद्यपि लागू करने के कई तरीके हैं, कहते हैं, एक लॉगिंग सुविधा, जिस तरह से आप इसका उपयोग करेंगे, वह समय में अपेक्षाकृत स्थिर होना चाहिए। यदि आप एक इंटरफ़ेस निकाल सकते हैं जो लॉगिंग की अवधारणा का प्रतिनिधित्व करता है, तो यह इंटरफ़ेस इसके कार्यान्वयन की तुलना में समय में बहुत अधिक स्थिर होना चाहिए, और कॉल लॉगिंग तंत्र को बनाए रखने या बढ़ाते समय आपके द्वारा किए जा सकने वाले परिवर्तनों से बहुत कम प्रभावित होना चाहिए।
कार्यान्वयन को एक इंटरफ़ेस पर निर्भर करते हुए, आपको रन-टाइम का चयन करने की संभावना मिलती है जो कार्यान्वयन आपके विशेष वातावरण के लिए बेहतर अनुकूल है। मामलों के आधार पर, यह दिलचस्प भी हो सकता है।
एजाइल सॉफ्टवेयर डेवलपमेंट, प्रिंसिपल्स, पैटर्न और प्रैक्टिस एंड एजाइल प्रिंसिपल्स, पैटर्न, और प्रैक्टिस इन सी # डिपेंडेंसी इनवर्जन प्रिंसिपल के पीछे मूल लक्ष्यों और प्रेरणाओं को पूरी तरह से समझने के लिए सबसे अच्छे संसाधन हैं। लेख "द डिपेंडेंसी इनवर्सन प्रिंसिपल" भी एक अच्छा संसाधन है, लेकिन इस तथ्य के कारण कि यह एक मसौदे का एक संक्षिप्त संस्करण है, जिसने अंततः पहले उल्लेखित पुस्तकों में अपना रास्ता बनाया, यह कुछ महत्वपूर्ण चर्चा की अवधारणा को छोड़ देता है पैकेज और इंटरफ़ेस का स्वामित्व जो इस सिद्धांत को "सामान्य से इंटरफ़ेस में प्रोग्राम करने के लिए, न कि कार्यान्वयन के लिए" डिजाइन पैटर्न (गामा, एट अल।) के भीतर पाए जाने वाले कार्यान्वयन के लिए महत्वपूर्ण हैं।
सारांश प्रदान करने के लिए, डिपेंडेंसी इनवर्सन प्रिंसिपल मुख्य रूप से "उच्च स्तर" घटकों से "निचले स्तर" घटकों के लिए निर्भरता की पारंपरिक दिशा को उलटने के बारे में है जैसे कि "निम्न स्तर" घटक "उच्च स्तर" के स्वामित्व वाले इंटरफेस पर निर्भर हैं। । (नोट: "उच्च स्तर" घटक यहां बाहरी निर्भरता / सेवाओं की आवश्यकता वाले घटक को संदर्भित करता है, जरूरी नहीं कि एक स्तरित वास्तुकला के भीतर इसकी वैचारिक स्थिति।) ऐसा करने में, युग्मन को इतना कम नहीं किया जाता है क्योंकि यह उन घटकों से स्थानांतरित किया जाता है जो सैद्धांतिक रूप से होते हैं। घटकों के लिए कम मूल्यवान जो सैद्धांतिक रूप से अधिक मूल्यवान हैं।
यह उन घटकों को डिजाइन करके हासिल किया जाता है जिनकी बाहरी निर्भरता एक इंटरफ़ेस के संदर्भ में व्यक्त की जाती है जिसके लिए घटक के उपभोक्ता द्वारा एक कार्यान्वयन प्रदान किया जाना चाहिए। दूसरे शब्दों में, परिभाषित इंटरफेस व्यक्त करते हैं कि घटक की क्या जरूरत है, न कि आप घटक का उपयोग कैसे करते हैं (उदाहरण के लिए "आईनडोमेसिएल", न कि "आईडीओसोमिंग")।
निर्भरता उलटा सिद्धांत क्या संदर्भित नहीं करता है, इंटरफेस के उपयोग के माध्यम से अमूर्त निर्भरता का सरल अभ्यास है (जैसे MyService → [ILogger ger Logger])। हालांकि यह निर्भरता के विशिष्ट कार्यान्वयन विवरण से एक घटक को विघटित करता है, यह उपभोक्ता और निर्भरता (जैसे [MyService → IMyServiceLogger] ger लकड़हारा के बीच संबंधों को उल्टा नहीं करता है।
निर्भरता उलटा सिद्धांत का महत्व सॉफ्टवेयर घटकों का पुन: उपयोग करने में सक्षम होने के एक विलक्षण लक्ष्य को पूरा किया जा सकता है जो उनकी कार्यक्षमता (लॉगिंग, सत्यापन, आदि) के एक हिस्से के लिए बाहरी निर्भरता पर निर्भर करते हैं।
पुन: उपयोग के इस सामान्य लक्ष्य के भीतर, हम पुन: उपयोग के दो उप-प्रकारों को चित्रित कर सकते हैं:
उप-निर्भरता कार्यान्वयन के साथ कई अनुप्रयोगों के भीतर एक सॉफ्टवेयर घटक का उपयोग करना (जैसे कि आपने एक डीआई कंटेनर विकसित किया है और लॉगिंग प्रदान करना चाहते हैं, लेकिन अपने कंटेनर को एक विशिष्ट लकड़हारे से युगल नहीं करना चाहते हैं, जो आपके कंटेनर का उपयोग करने वाले सभी को भी करना है अपनी चुनी हुई लॉगिंग लाइब्रेरी का उपयोग करें)।
एक विकसित संदर्भ के भीतर सॉफ्टवेयर घटकों का उपयोग करना (जैसे आपने व्यावसायिक-तर्क घटक विकसित किए हैं जो एक एप्लिकेशन के कई संस्करणों में समान रहते हैं जहां कार्यान्वयन विवरण विकसित हो रहे हैं)।
कई अनुप्रयोगों में घटकों के पुन: उपयोग के पहले मामले के साथ, जैसे कि इन्फ्रास्ट्रक्चर लाइब्रेरी के साथ, लक्ष्य यह है कि इस तरह की निर्भरता पर निर्भरता लेने के बाद से अपने उपभोक्ताओं को अपने स्वयं के पुस्तकालय की उप-निर्भरता के लिए युग्मित किए बिना आपके उपभोक्ताओं को एक कोर इन्फ्रास्ट्रक्चर की आवश्यकता हो। उपभोक्ताओं को समान निर्भरता की भी आवश्यकता होती है। यह समस्याग्रस्त हो सकता है जब आपके पुस्तकालय के उपभोक्ता एक ही बुनियादी ढांचे की जरूरतों (जैसे NLog बनाम log4net) के लिए एक अलग पुस्तकालय का उपयोग करना चुनते हैं, या यदि वे आवश्यक पुस्तकालय के बाद के संस्करण का उपयोग करना चुनते हैं जो संस्करण के साथ पीछे नहीं है अपने पुस्तकालय द्वारा आवश्यक।
व्यवसाय-तर्क घटकों (अर्थात "उच्च-स्तरीय घटकों") के पुन: उपयोग के दूसरे मामले के साथ, लक्ष्य आपके आवेदन विवरण के मूल डोमेन कार्यान्वयन को आपके कार्यान्वयन विवरणों की बदलती जरूरतों (यानी हठ पुस्तकालयों को बदलते / उन्नत / अपग्रेड करना) से अलग करना है। , एन्क्रिप्शन रणनीतियों, आदि)। आदर्श रूप से, किसी एप्लिकेशन के कार्यान्वयन विवरण को बदलने से एप्लिकेशन के व्यावसायिक तर्क को समझने वाले घटकों को नहीं तोड़ना चाहिए।
नोट: कुछ इस दूसरे मामले को वास्तविक पुन: उपयोग के रूप में वर्णित करने पर आपत्ति कर सकते हैं, तर्क है कि एकल-विकसित अनुप्रयोग के भीतर उपयोग किए जाने वाले व्यावसायिक-तर्क घटकों जैसे घटक केवल एक ही उपयोग का प्रतिनिधित्व करते हैं। हालांकि, यह विचार यह है कि अनुप्रयोग के कार्यान्वयन विवरण में प्रत्येक परिवर्तन एक नया संदर्भ प्रदान करता है और इसलिए एक अलग उपयोग मामला है, हालांकि अंतिम लक्ष्यों को अलगाव बनाम पोर्टेबिलिटी के रूप में प्रतिष्ठित किया जा सकता है।
इस दूसरे मामले में निर्भरता उलटा सिद्धांत का पालन करते हुए, कुछ लाभ की पेशकश कर सकते हैं, यह ध्यान दिया जाना चाहिए कि जावा और सी # जैसी आधुनिक भाषाओं पर लागू इसका मूल्य बहुत कम हो गया है, शायद अप्रासंगिक होने की बात है। जैसा कि पहले चर्चा की गई थी, डीआईपी में कार्यान्वयन विवरणों को अलग-अलग पैकेजों में पूरी तरह से अलग करना शामिल है। एक विकसित अनुप्रयोग के मामले में, हालांकि, व्यवसाय विस्तार के संदर्भ में परिभाषित इंटरफेस का उपयोग करना कार्यान्वयन कार्यान्वयन घटकों की बदलती जरूरतों के कारण उच्च-स्तरीय घटकों को संशोधित करने की आवश्यकता के खिलाफ होगा, भले ही कार्यान्वयन विवरण अंततः एक ही पैकेज के भीतर रहते हों। । सिद्धांत का यह भाग उन पहलुओं को दर्शाता है जो उस भाषा को देखने के लिए प्रासंगिक थे जब सिद्धांत को संहिताबद्ध किया गया था (यानी C ++) जो नई भाषाओं के लिए प्रासंगिक नहीं हैं। ने कहा कि,
इस सिद्धांत की एक लंबी चर्चा के रूप में यह इंटरफेस, डिपेंडेंसी इंजेक्शन, और अलग इंटरफ़ेस पैटर्न के सरल उपयोग से संबंधित है यहां पाया जा सकता है । इसके अतिरिक्त, इस बात की एक चर्चा है कि सिद्धांत गतिशील रूप से टाइप की जाने वाली भाषाओं जैसे कि जावास्क्रिप्ट से कैसे संबंधित हो सकते हैं ।
जब हम सॉफ़्टवेयर एप्लिकेशन डिज़ाइन करते हैं, तो हम निम्न स्तर की कक्षाओं पर विचार कर सकते हैं जो बुनियादी और प्राथमिक संचालन (डिस्क एक्सेस, नेटवर्क प्रोटोकॉल, ...) को लागू करती हैं और उच्च स्तर की कक्षाएं उन कक्षाओं को जोड़ती हैं जो जटिल तर्क (व्यावसायिक प्रवाह, ...) को बाधित करती हैं।
अंतिम वाले निम्न स्तर की कक्षाओं पर निर्भर करते हैं। इस तरह की संरचनाओं को लागू करने का एक प्राकृतिक तरीका निम्न स्तर की कक्षाएं लिखना होगा और एक बार हमारे पास जटिल उच्च स्तरीय कक्षाएं लिखने के लिए होगा। चूंकि उच्च स्तरीय कक्षाएं दूसरों के संदर्भ में परिभाषित की जाती हैं, ऐसा करने का तार्किक तरीका लगता है। लेकिन यह एक लचीला डिजाइन नहीं है। यदि हमें निम्न स्तर के वर्ग को बदलने की आवश्यकता है तो क्या होगा?
निर्भरता उलटा सिद्धांत बताता है कि:
यह सिद्धांत पारंपरिक धारणा को "उल्टा" करना चाहता है कि सॉफ्टवेयर में उच्च स्तर के मॉड्यूल को निचले स्तर के मॉड्यूल पर निर्भर होना चाहिए। यहाँ उच्च स्तर के मॉड्यूल अमूर्त (उदाहरण के लिए, इंटरफ़ेस के तरीकों को तय करना) के मालिक हैं, जो निचले स्तर के मॉड्यूल द्वारा कार्यान्वित किए जाते हैं। इस प्रकार निचले स्तर के मॉड्यूल को उच्च स्तर के मॉड्यूल पर निर्भर करना।
निर्भरता उलटा अच्छी तरह से लागू आपके आवेदन की पूरी वास्तुकला के स्तर पर लचीलापन और स्थिरता देता है। यह आपके एप्लिकेशन को अधिक सुरक्षित और स्थिर रूप से विकसित करने की अनुमति देगा।
परंपरागत रूप से एक स्तरित वास्तुकला यूआई व्यावसायिक परत पर निर्भर करता था और यह बदले में डेटा एक्सेस परत पर निर्भर करता था।
आपको लेयर, पैकेज या लाइब्रेरी को समझना होगा। देखते हैं कि कोड कैसा होगा।
हमारे पास डेटा एक्सेस लेयर के लिए एक लाइब्रेरी या पैकेज होगा।
// DataAccessLayer.dll
public class ProductDAO {
}
और एक अन्य लाइब्रेरी या पैकेज लेयर बिजनेस लॉजिक जो डेटा एक्सेस लेयर पर निर्भर करता है।
// BusinessLogicLayer.dll
using DataAccessLayer;
public class ProductBO {
private ProductDAO productDAO;
}
निर्भरता उलटा निम्न इंगित करता है:
उच्च-स्तरीय मॉड्यूल को निम्न-स्तर के मॉड्यूल पर निर्भर नहीं होना चाहिए। दोनों को अमूर्तता पर निर्भर होना चाहिए।
विवरण विवरण पर निर्भर नहीं होना चाहिए। विवरण अमूर्तता पर निर्भर होना चाहिए।
उच्च-स्तरीय मॉड्यूल और निम्न स्तर क्या हैं? लाइब्रेरी या पैकेज जैसे सोच मॉड्यूल, उच्च-स्तरीय मॉड्यूल वे होंगे जो परंपरागत रूप से निर्भरता और निम्न स्तर पर होते हैं, जिस पर वे निर्भर करते हैं।
दूसरे शब्दों में, मॉड्यूल उच्च स्तर वह होगा जहां कार्रवाई को लागू किया जाता है और निम्न स्तर जहां कार्रवाई की जाती है।
इस सिद्धांत से आकर्षित करने के लिए एक उचित निष्कर्ष यह है कि सहमति के बीच कोई निर्भरता नहीं होनी चाहिए, लेकिन एक अमूर्तता पर निर्भरता होनी चाहिए। लेकिन हम जो दृष्टिकोण अपनाते हैं, उसके अनुसार हम निवेश को गलत तरीके से निर्भर करते हैं, लेकिन एक अमूर्तता हो सकती है।
कल्पना करें कि हम अपना कोड इस प्रकार अनुकूलित करते हैं:
हमारे पास डेटा एक्सेस लेयर के लिए एक लाइब्रेरी या पैकेज होगा जो अमूर्तता को परिभाषित करता है।
// DataAccessLayer.dll
public interface IProductDAO
public class ProductDAO : IProductDAO{
}
और एक अन्य लाइब्रेरी या पैकेज लेयर बिजनेस लॉजिक जो डेटा एक्सेस लेयर पर निर्भर करता है।
// BusinessLogicLayer.dll
using DataAccessLayer;
public class ProductBO {
private IProductDAO productDAO;
}
यद्यपि हम व्यापार और डेटा पहुंच के बीच एक अमूर्त निर्भरता पर निर्भर हैं।
निर्भरता व्युत्क्रम प्राप्त करने के लिए, दृढ़ता इंटरफ़ेस को मॉड्यूल या पैकेज में परिभाषित किया जाना चाहिए जहां यह उच्च स्तरीय तर्क या डोमेन है और निम्न-स्तरीय मॉड्यूल में नहीं है।
पहले परिभाषित करें कि डोमेन लेयर क्या है और इसके संचार का अमूर्त होना दृढ़ता को परिभाषित करता है।
// Domain.dll
public interface IProductRepository;
using DataAccessLayer;
public class ProductBO {
private IProductRepository productRepository;
}
दृढ़ता परत डोमेन पर निर्भर करता है, के बाद अब उलटा हो रही है अगर एक निर्भरता परिभाषित किया गया है।
// Persistence.dll
public class ProductDAO : IProductRepository{
}
(स्रोत: xurxodev.com )
उद्देश्य और लाभों को गहरा करते हुए, अवधारणा को अच्छी तरह से आत्मसात करना महत्वपूर्ण है। यदि हम यंत्रवत् रूप से रहते हैं और विशिष्ट केस रिपॉजिटरी सीखते हैं, तो हम यह पहचान नहीं कर पाएंगे कि हम निर्भरता के सिद्धांत को कहां लागू कर सकते हैं।
लेकिन हम एक निर्भरता को उल्टा क्यों करते हैं? विशिष्ट उदाहरणों से परे मुख्य उद्देश्य क्या है?
इस तरह की आमतौर पर सबसे स्थिर चीजों की अनुमति होती है, जो कम स्थिर चीजों पर निर्भर नहीं होती हैं, अधिक बार बदलने के लिए।
हठ के साथ संवाद करने के लिए डिज़ाइन किए गए डोमेन लॉजिक या क्रियाओं की तुलना में एक ही डेटाबेस तक पहुंचने के लिए या तो डेटाबेस या तकनीक को बदलना आसान है। इस वजह से, निर्भरता उलट जाती है क्योंकि अगर यह परिवर्तन होता है तो दृढ़ता को बदलना आसान होता है। इस तरह हमें डोमेन नहीं बदलना पड़ेगा। डोमेन परत सभी में सबसे अधिक स्थिर है, यही कारण है कि इसे किसी भी चीज पर निर्भर नहीं होना चाहिए।
लेकिन सिर्फ यह रिपॉजिटरी उदाहरण नहीं है। ऐसे कई परिदृश्य हैं जहां यह सिद्धांत लागू होता है और इस सिद्धांत पर आधारित आर्किटेक्चर हैं।
ऐसे आर्किटेक्चर हैं जहां निर्भरता व्युत्क्रम इसकी परिभाषा के लिए महत्वपूर्ण है। सभी डोमेन में यह सबसे महत्वपूर्ण है और यह वह सार है जो डोमेन के बीच संचार प्रोटोकॉल को इंगित करेगा और बाकी पैकेज या लाइब्रेरी परिभाषित हैं।
में स्वच्छ वास्तुकला डोमेन केंद्र में स्थित है और यदि आप तीर निर्भरता का संकेत की दिशा में देखो, यह स्पष्ट है क्या सबसे महत्वपूर्ण और स्थिर परतें हैं। बाहरी परतों को अस्थिर उपकरण माना जाता है, इसलिए उनके आधार पर बचें।
(स्रोत: 8 वें.कॉम )
यह हेक्सागोनल वास्तुकला के साथ उसी तरह से होता है, जहां डोमेन मध्य भाग में भी स्थित है और पोर्ट डोमिनोज़ आउटवर्ड से संचार के सार हैं। यहां फिर से यह स्पष्ट है कि डोमेन सबसे स्थिर है और पारंपरिक निर्भरता उलटा है।
मेरे लिए, डिपेंडेंसी इनवर्सन प्रिंसिपल, जैसा कि आधिकारिक लेख में वर्णित है वास्तव में कम पुन: प्रयोज्य होने वाले मॉड्यूल की पुन: प्रयोज्य को बढ़ाने के लिए एक भ्रामक प्रयास है, साथ ही सी ++ भाषा में एक समस्या को हल करने का एक तरीका है।
C ++ में समस्या यह है कि हेडर फ़ाइलों में आमतौर पर निजी क्षेत्रों और विधियों की घोषणाएं होती हैं। इसलिए, यदि एक उच्च-स्तरीय C ++ मॉड्यूल में निम्न-स्तरीय मॉड्यूल के लिए हेडर फ़ाइल शामिल है, तो यह उस मॉड्यूल के वास्तविक कार्यान्वयन विवरण पर निर्भर करेगा । और यह, जाहिर है, अच्छी बात नहीं है। लेकिन यह आमतौर पर इस्तेमाल की जाने वाली अधिक आधुनिक भाषाओं में एक मुद्दा नहीं है।
उच्च-स्तरीय मॉड्यूल निम्न-स्तर के मॉड्यूल की तुलना में स्वाभाविक रूप से कम पुन: प्रयोज्य हैं क्योंकि पूर्व सामान्य रूप से उत्तरार्द्ध की तुलना में अधिक अनुप्रयोग / संदर्भ हैं। उदाहरण के लिए, यूआई स्क्रीन को लागू करने वाला एक घटक उच्चतम स्तर का है और यह भी बहुत (पूरी तरह से?) अनुप्रयोग के लिए विशिष्ट है। एक अलग अनुप्रयोग में इस तरह के एक घटक का पुन: उपयोग करने का प्रयास काउंटर-उत्पादक है, और केवल ओवर-इंजीनियरिंग के लिए नेतृत्व कर सकता है।
तो, एक घटक A के समान स्तर पर एक अलग अमूर्त का निर्माण एक घटक B पर निर्भर करता है (जो A पर निर्भर नहीं करता है) केवल तभी किया जा सकता है जब घटक A विभिन्न अनुप्रयोगों या संदर्भों में पुन: उपयोग के लिए उपयोगी होगा। अगर ऐसा नहीं है, तो डीआईपी लागू करना खराब डिजाइन होगा।
मूल रूप से यह कहता है:
क्लास को एब्स्ट्रैक्ट (उदाहरण के लिए इंटरफ़ेस, एब्सट्रैक्ट क्लास) पर निर्भर होना चाहिए, न कि विशिष्ट विवरण (कार्यान्वयन) पर।
अच्छे उत्तर और अच्छे उदाहरण पहले से ही यहां दूसरों द्वारा दिए गए हैं।
कारण DIP महत्वपूर्ण है क्योंकि यह OO-सिद्धांत 'शिथिल युग्मित डिजाइन "सुनिश्चित करता है।
आपके सॉफ़्टवेयर की वस्तुओं को एक पदानुक्रम में नहीं जाना चाहिए जहां कुछ ऑब्जेक्ट शीर्ष-स्तर वाले होते हैं, जो निम्न-स्तर की वस्तुओं पर निर्भर होते हैं। निम्न-स्तरीय ऑब्जेक्ट में परिवर्तन तब आपके शीर्ष-स्तरीय ऑब्जेक्ट में रिपल-थ्रू होगा जो सॉफ़्टवेयर को परिवर्तन के लिए बहुत नाजुक बना देता है।
आप चाहते हैं कि आपकी 'टॉप-लेवल' वस्तुएं बहुत स्थिर हों और बदलाव के लिए नाजुक न हों, इसलिए आपको निर्भरता को उलटने की जरूरत है।
डिपेंडेंसी इनवर्जन प्रिंसिपल को बताने का एक बहुत ही स्पष्ट तरीका है:
आपके मॉड्यूल जो जटिल व्यावसायिक तर्क को अतिक्रमण करते हैं, उन्हें अन्य मॉड्यूल पर सीधे निर्भर नहीं होना चाहिए जो व्यावसायिक तर्क को एन्क्रिप्ट करते हैं। इसके बजाय, उन्हें केवल साधारण डेटा के इंटरफेस पर निर्भर होना चाहिए।
अपने वर्ग Logic
को लागू करने के बजाय, जैसा कि आमतौर पर लोग करते हैं:
class Dependency { ... }
class Logic {
private Dependency dep;
int doSomething() {
// Business logic using dep here
}
}
आपको कुछ इस तरह करना चाहिए:
class Dependency { ... }
interface Data { ... }
class DataFromDependency implements Data {
private Dependency dep;
...
}
class Logic {
int doSomething(Data data) {
// compute something with data
}
}
Data
और DataFromDependency
उसी मॉड्यूल में रहना चाहिए, जैसे कि Logic
नहीं Dependency
।
यह क्यों?
Dependency
परिवर्तन होता है, तो आपको बदलने की आवश्यकता नहीं होती है Logic
।Logic
बहुत सरल कार्य है: यह केवल उसी पर संचालित होता है जो ADT जैसा दिखता है।Logic
अब और अधिक आसानी से परीक्षण किया जा सकता है। अब आप सीधे Data
फर्जी डेटा के साथ सीधे प्रवेश कर सकते हैं और इसे पास कर सकते हैं । मॉक या जटिल परीक्षण मचान की आवश्यकता नहीं है।DataFromDependency
, जो सीधे संदर्भ देता है Dependency
, उसी मॉड्यूल में है Logic
, तो Logic
मॉड्यूल अभी भी सीधे Dependency
संकलन समय पर मॉड्यूल पर निर्भर करता है । सिद्धांत के प्रति अंकल बॉब का स्पष्टीकरण , इससे बचना डीआईपी का पूरा बिंदु है। बल्कि, डीआईपी का पालन करने के लिए, Data
समान मॉड्यूल में होना चाहिए Logic
, लेकिन उसी मॉड्यूल में DataFromDependency
होना चाहिए Dependency
।
नियंत्रण का उलटा (IoC) एक डिजाइन पैटर्न है जहां किसी वस्तु को अपनी निर्भरता के लिए एक रूपरेखा पूछने के बजाय एक बाहरी ढांचे द्वारा अपनी निर्भरता सौंप दी जाती है।
पारंपरिक लुकअप का उपयोग करते हुए स्यूडोकोड उदाहरण:
class Service {
Database database;
init() {
database = FrameworkSingleton.getService("database");
}
}
IoC का उपयोग करने वाला समान कोड:
class Service {
Database database;
init(database) {
this.database = database;
}
}
IoC के लाभ हैं:
निर्भरता उलटा का बिंदु पुन: प्रयोज्य सॉफ्टवेयर बनाना है।
विचार यह है कि कोड के दो टुकड़े एक-दूसरे पर निर्भर होने के बजाय, वे कुछ सार इंटरफेस पर भरोसा करते हैं। फिर आप दूसरे के बिना या तो टुकड़े का पुन: उपयोग कर सकते हैं।
जिस तरह से यह सबसे अधिक प्राप्त किया जाता है वह जावा में स्प्रिंग जैसे नियंत्रण (IoC) कंटेनर के व्युत्क्रम के माध्यम से होता है। इस मॉडल में, वस्तुओं के गुणों को एक्सएमएल कॉन्फ़िगरेशन के माध्यम से सेट किया जाता है बजाय वस्तुओं के बाहर जाने और उनकी निर्भरता खोजने के लिए।
इस छद्मकोड की कल्पना करें ...
public class MyClass
{
public Service myService = ServiceLocator.service;
}
MyClass सीधे सेवा वर्ग और ServiceLocator वर्ग दोनों पर निर्भर करता है। यदि आप इसे किसी अन्य एप्लिकेशन में उपयोग करना चाहते हैं तो इसे उन दोनों की आवश्यकता है। अब जरा इसकी कल्पना करें ...
public class MyClass
{
public IService myService;
}
अब, MyClass एक इंटरफ़ेस, IService इंटरफ़ेस पर निर्भर करता है। हम IoC कंटेनर को वास्तव में उस वैरिएबल का मान सेट करने देंगे।
तो अब, MyClass को अन्य परियोजनाओं में आसानी से पुन: उपयोग किया जा सकता है, बिना उन अन्य दो वर्गों की निर्भरता को लाने के साथ।
इससे भी बेहतर, आपको MyService की निर्भरता, और उन निर्भरताओं की निर्भरता, और ... अच्छी तरह से खींचने की ज़रूरत नहीं है, आपको यह विचार मिलता है।
यदि हम इसे एक दिए गए के रूप में ले सकते हैं कि एक निगम में "उच्च स्तर" कर्मचारी को उनकी योजनाओं के निष्पादन के लिए भुगतान किया जाता है, और ये कि कई "निम्न स्तर" कर्मचारी की योजनाओं के समग्र निष्पादन द्वारा वितरित किए जाते हैं, तो हम कह सकते हैं यह आम तौर पर एक भयानक योजना है अगर किसी भी तरह से उच्च स्तर के कर्मचारी की योजना विवरण किसी भी निचले स्तर के कर्मचारी की विशिष्ट योजना के लिए युग्मित है।
यदि एक उच्च स्तर के कार्यकारी के पास "डिलीवरी के समय में सुधार" करने की योजना है, और यह इंगित करता है कि शिपिंग लाइन में एक कर्मचारी को प्रत्येक सुबह कॉफी और स्ट्रेच करना चाहिए, तो वह योजना अत्यधिक युग्मित है और कम सामंजस्य है। लेकिन अगर योजना किसी विशिष्ट कर्मचारी का कोई उल्लेख नहीं करती है, और वास्तव में "बस एक इकाई की आवश्यकता होती है जो काम करने के लिए तैयार हो सकती है", तो योजना शिथिल रूप से युग्मित है और अधिक सामंजस्यपूर्ण है: योजनाएं ओवरलैप नहीं होती हैं और आसानी से प्रतिस्थापित की जा सकती हैं । ठेकेदार, या रोबोट, आसानी से कर्मचारियों को बदल सकते हैं और उच्च स्तर की योजना अपरिवर्तित रहती है।
निर्भरता व्युत्क्रम सिद्धांत में "उच्च स्तर" का अर्थ है "अधिक महत्वपूर्ण"।
मैं देख सकता हूँ कि उपरोक्त उत्तरों में अच्छी व्याख्या दी गई है। हालाँकि मैं सरल उदाहरण के साथ कुछ आसान स्पष्टीकरण प्रदान करना चाहता हूं।
निर्भरता उलटा सिद्धांत प्रोग्रामर को हार्डकोडेड निर्भरता को दूर करने की अनुमति देता है ताकि आवेदन शिथिल रूप से युग्मित और विस्तार योग्य हो जाए।
इसे कैसे प्राप्त करें: अमूर्तता के माध्यम से
बिना निर्भरता के उलटा:
class Student {
private Address address;
public Student() {
this.address = new Address();
}
}
class Address{
private String perminentAddress;
private String currentAdrress;
public Address() {
}
}
उपरोक्त कोड स्निपेट में, पता ऑब्जेक्ट हार्ड-कोडेड है। इसके बजाय अगर हम निर्भरता व्युत्क्रम का उपयोग कर सकते हैं और कंस्ट्रक्टर या सेटर विधि से गुजरकर एड्रेस ऑब्जेक्ट को इंजेक्ट कर सकते हैं। चलो देखते हैं।
निर्भरता उलटा के साथ:
class Student{
private Address address;
public Student(Address address) {
this.address = address;
}
//or
public void setAddress(Address address) {
this.address = address;
}
}
निर्भरता का उलटा: सार पर निर्भर करता है, न कि सहमति पर।
नियंत्रण का उलटा: मुख्य बनाम अमूर्त, और मुख्य प्रणालियों का गोंद कैसे है।
ये कुछ अच्छी पोस्ट हैं जो इस बारे में बात कर रही हैं:
https://coderstower.com/2019/03/26/dependency-inversion-why-you-shouldnt-avoid-it/
https://coderstower.com/2019/04/02/main-and-abstraction-the-decoupled-peers/
https://coderstower.com/2019/04/09/inversion-of-control-putting-all-together/
मान लीजिए कि हमारे पास दो वर्ग हैं: Engineer
और Programmer
:
क्लास इंजीनियर की प्रोग्रामर क्लास पर निर्भरता होती है, जैसे नीचे:
class Engineer () {
fun startWork(programmer: Programmer){
programmer.work()
}
}
class Programmer {
fun work(){
//TODO Do some work here!
}
}
इस उदाहरण में कक्षा Engineer
का हमारी Programmer
कक्षा पर निर्भरता है । अगर मुझे बदलने की जरूरत है तो क्या होगा Programmer
?
जाहिर है मुझे Engineer
भी बदलने की जरूरत है। (वाह, इस बिंदु OCP
पर भी उल्लंघन किया जाता है)
फिर, इस गंदगी को साफ करने के लिए हमें क्या करना होगा? जवाब वास्तव में अमूर्त है। अमूर्तन द्वारा, हम इन दो वर्गों के बीच निर्भरता को दूर कर सकते हैं। उदाहरण के लिए, मैं Interface
प्रोग्रामर वर्ग के लिए बना सकता हूं और अब से हर वर्ग जो इसका उपयोग करना चाहता है, Programmer
उसका उपयोग करना है Interface
, फिर प्रोग्रामर वर्ग को बदलकर, हमें किसी भी वर्ग को बदलने की आवश्यकता नहीं है जो इसका इस्तेमाल करते हैं, क्योंकि हम अमूर्तता के कारण हैं। उपयोग किया गया।
नोट: DependencyInjection
हमें DIP
और SRP
भी करने में मदद कर सकता है ।
आम तौर पर अच्छे उत्तरों की हड़बड़ी में, मैं अच्छा बनाम बुरा अभ्यास प्रदर्शित करने के लिए अपना खुद का एक छोटा सा नमूना जोड़ना चाहूंगा। और हाँ, मैं पत्थर फेंकने वाला नहीं हूँ!
कहते हैं, आप कंसोल I / O के माध्यम से एक स्ट्रिंग को बेस 64 प्रारूप में बदलने के लिए एक छोटा कार्यक्रम चाहते हैं । यहाँ भोली दृष्टिकोण है:
class Program
{
static void Main(string[] args)
{
/*
* BadEncoder: High-level class *contains* low-level I/O functionality.
* Hence, you'll have to fiddle with BadEncoder whenever you want to change
* the I/O mode or details. Not good. A good encoder should be I/O-agnostic --
* problems with I/O shouldn't break the encoder!
*/
BadEncoder.Run();
}
}
public static class BadEncoder
{
public static void Run()
{
Console.WriteLine(Convert.ToBase64String(Encoding.UTF8.GetBytes(Console.ReadLine())));
}
}
डीआईपी मूल रूप से कहता है कि उच्च-स्तरीय घटकों को निम्न-स्तर के कार्यान्वयन पर निर्भर नहीं होना चाहिए, जहां रॉबर्ट सी मार्टिन ("क्लीन आर्किटेक्चर") के अनुसार "स्तर" I / O से दूरी है। लेकिन आप इस भविष्यवाणी से कैसे बाहर निकलते हैं? बस केंद्रीय एनकोडर को केवल उन लोगों को परेशान किए बिना इंटरफेस पर निर्भर बनाकर कैसे लागू किया जाता है:
class Program
{
static void Main(string[] args)
{
/* Demo of the Dependency Inversion Principle (= "High-level functionality
* should not depend upon low-level implementations"):
* You can easily implement new I/O methods like
* ConsoleReader, ConsoleWriter without ever touching the high-level
* Encoder class!!!
*/
GoodEncoder.Run(new ConsoleReader(), new ConsoleWriter()); }
}
public static class GoodEncoder
{
public static void Run(IReadable input, IWriteable output)
{
output.WriteOutput(Convert.ToBase64String(Encoding.ASCII.GetBytes(input.ReadInput())));
}
}
public interface IReadable
{
string ReadInput();
}
public interface IWriteable
{
void WriteOutput(string txt);
}
public class ConsoleReader : IReadable
{
public string ReadInput()
{
return Console.ReadLine();
}
}
public class ConsoleWriter : IWriteable
{
public void WriteOutput(string txt)
{
Console.WriteLine(txt);
}
}
ध्यान दें कि GoodEncoder
I / O मोड को बदलने के लिए आपको स्पर्श करने की आवश्यकता नहीं है - यह वर्ग उस I / O इंटरफेस से खुश है जो इसे जानता है; किसी भी निम्न-स्तरीय कार्यान्वयन IReadable
और IWriteable
इसे कभी परेशान नहीं करेंगे।
GoodEncoder
आपके दूसरे उदाहरण पर निर्भर नहीं करते हैं । डीआईपी उदाहरण बनाने के लिए, आपको अपने द्वारा निकाले गए इंटरफेस के "मालिक" के बारे में एक धारणा प्रस्तुत करने की आवश्यकता है - और विशेष रूप से, उन्हें गुडइंकोडर के समान पैकेज में रखने के लिए, जबकि उनका कार्यान्वयन बाहर रहता है।
निर्भरता उलटा सिद्धांत (डीआईपी) का कहना है कि
i) उच्च स्तर के मॉड्यूल को निम्न-स्तर के मॉड्यूल पर निर्भर नहीं होना चाहिए। दोनों को अमूर्तता पर निर्भर होना चाहिए।
ii) अंश कभी भी विवरण पर निर्भर नहीं होना चाहिए। विवरण अमूर्तता पर निर्भर होना चाहिए।
उदाहरण:
public interface ICustomer
{
string GetCustomerNameById(int id);
}
public class Customer : ICustomer
{
//ctor
public Customer(){}
public string GetCustomerNameById(int id)
{
return "Dummy Customer Name";
}
}
public class CustomerFactory
{
public static ICustomer GetCustomerData()
{
return new Customer();
}
}
public class CustomerBLL
{
ICustomer _customer;
public CustomerBLL()
{
_customer = CustomerFactory.GetCustomerData();
}
public string GetCustomerNameById(int id)
{
return _customer.GetCustomerNameById(id);
}
}
public class Program
{
static void Main()
{
CustomerBLL customerBLL = new CustomerBLL();
int customerId = 25;
string customerName = customerBLL.GetCustomerNameById(customerId);
Console.WriteLine(customerName);
Console.ReadKey();
}
}
नोट: क्लास को इंटरफ़ेस या अमूर्त कक्षाओं जैसे सार पर निर्भर होना चाहिए, न कि विशिष्ट विवरण (इंटरफ़ेस का कार्यान्वयन) पर।