सबसे पहले, मैं इस उत्तर के लिए एक धारणा की व्याख्या करना चाहता हूं। यह हमेशा सच नहीं होता है, लेकिन अक्सर:
इंटरफेस विशेषण हैं; कक्षाएं संज्ञा हैं।
(वास्तव में, ऐसे इंटरफेस हैं जो संज्ञा भी हैं, लेकिन मैं यहां सामान्य करना चाहता हूं।)
इसलिए, उदाहरण के लिए एक इंटरफ़ेस कुछ इस तरह के IDisposable
, IEnumerable
या हो सकता है IPrintable
। एक वर्ग इन इंटरफेसों में से एक या अधिक का वास्तविक कार्यान्वयन है: List
याMap
दोनों कार्यान्वयन हो सकते हैं IEnumerable
।
बिंदु पाने के लिए: अक्सर आपकी कक्षाएं एक-दूसरे पर निर्भर करती हैं। उदाहरण के लिए, आपके पास एक ऐसा Database
वर्ग हो सकता है जो आपके डेटाबेस तक पहुँच सके (hah, आश्चर्य! ;-)), लेकिन आप यह भी चाहते हैं कि यह वर्ग डेटाबेस तक पहुँचने के बारे में लॉगिंग करे। मान लीजिए कि आपके पास एक और वर्ग है Logger
, तो Database
निर्भरता है Logger
।
अब तक सब ठीक है।
आप Database
निम्न लाइन के साथ अपनी कक्षा के अंदर इस निर्भरता को मॉडल कर सकते हैं :
var logger = new Logger();
और सब कुछ ठीक है। यह उस दिन तक ठीक है जब आपको पता चलता है कि आपको लॉगर्स का एक गुच्छा चाहिए: कभी-कभी आप कंसोल में लॉग इन करना चाहते हैं, कभी-कभी फाइल सिस्टम में, कभी-कभी टीसीपी / आईपी और रिमोट लॉगिंग सर्वर का उपयोग करके, और इसी तरह ...
और निश्चित रूप से आप अपने सभी कोड को बदलना नहीं चाहते हैं (इस बीच आपके पास गजिलियन हैं) और सभी लाइनों को बदल दें
var logger = new Logger();
द्वारा:
var logger = new TcpLogger();
पहला, यह कोई मज़ा नहीं है। दूसरा, यह त्रुटि-प्रवण है। तीसरा, यह एक प्रशिक्षित बंदर के लिए मूर्खतापूर्ण, दोहराव वाला काम है। तो तुम क्या करते हो?
जाहिर है कि एक इंटरफ़ेस ICanLog
(या समान) को पेश करना काफी अच्छा विचार है जो सभी विभिन्न लॉगर द्वारा कार्यान्वित किया जाता है। तो आपके कोड में चरण 1 यह है कि आप क्या करें:
ICanLog logger = new Logger();
अब टाइप इंफ़ेक्शन किसी भी प्रकार से नहीं बदलता है, आपके पास हमेशा एक एकल इंटरफ़ेस होता है जिसके विरुद्ध विकसित होता है। अगला कदम यह है कि आप बार-बार नहीं चाहते हैं new Logger()
। तो आप एक एकल, केंद्रीय कारखाने वर्ग में नए उदाहरण बनाने के लिए विश्वसनीयता डालते हैं, और आपको कोड मिलता है जैसे:
ICanLog logger = LoggerFactory.Create();
फैक्ट्री खुद तय करती है कि किस तरह का लकड़हारा बनाना है। आपका कोड किसी भी लंबे समय तक देखभाल नहीं करता है, और यदि आप उपयोग किए जा रहे लकड़हारे के प्रकार को बदलना चाहते हैं, तो आप इसे एक बार बदलते हैं : कारखाने के अंदर।
अब, निश्चित रूप से, आप इस कारखाने को सामान्य कर सकते हैं, और इसे किसी भी प्रकार के लिए काम कर सकते हैं:
ICanLog logger = TypeFactory.Create<ICanLog>();
कहीं न कहीं इस टाइपफैक्ट को कॉन्फ़िगरेशन डेटा की आवश्यकता होती है जो एक विशिष्ट इंटरफ़ेस प्रकार का अनुरोध करने पर वास्तविक क्लास को तुरंत शुरू करने के लिए होता है, इसलिए आपको मैपिंग की आवश्यकता होती है। बेशक आप इस मैपिंग को अपने कोड के अंदर कर सकते हैं, लेकिन फिर एक प्रकार का बदलाव का मतलब है कि recompiling। लेकिन आप इस मैपिंग को XML फाइल के अंदर भी डाल सकते हैं, जैसे। यह आपको संकलन समय (!) के बाद भी वास्तव में उपयोग किए जाने वाले वर्ग को बदलने की अनुमति देता है, इसका अर्थ है कि गतिशील रूप से, बिना रीकॉम्पेलिंग के!
इसके लिए आपको एक उपयोगी उदाहरण देने के लिए: एक ऐसे सॉफ़्टवेयर के बारे में सोचें जो सामान्य रूप से लॉग इन नहीं करता है, लेकिन जब आपका ग्राहक कॉल करता है और मदद मांगता है क्योंकि उसे कोई समस्या होती है, तो आप उसे जो भी भेजते हैं वह अपडेटेड XML कॉन्फिगर फाइल है, और अब उसके पास है लॉगिंग सक्षम, और आपका समर्थन आपके ग्राहकों की मदद करने के लिए लॉग फ़ाइलों का उपयोग कर सकता है।
और अब, जब आप नामों को थोड़ा बदल देते हैं, तो आप एक सेवा लोकेटर के एक सरल कार्यान्वयन के साथ समाप्त होते हैं , जो कि इनवर्टर ऑफ कंट्रोल के लिए दो पैटर्न में से एक है (चूंकि आप इनवर्ट कंट्रोल करते हैं कि कौन सटीक वर्ग को तुरंत बताता है)।
इस सब में आपके कोड में निर्भरता कम हो जाती है, लेकिन अब आपके सभी कोड में केंद्रीय, एकल सेवा लोकेटर पर निर्भरता है।
निर्भरता इंजेक्शन अब इस पंक्ति का अगला चरण है: बस सेवा लोकेटर के लिए इस एकल निर्भरता से छुटकारा पाएं: विभिन्न वर्गों के बजाय सेवा लोकेटर को एक विशिष्ट इंटरफ़ेस के लिए कार्यान्वयन के लिए पूछ रहे हैं, आप - एक बार फिर से - जो तुरंत क्या पर नियंत्रण वापस ।
निर्भरता इंजेक्शन के साथ, आपकी Database
कक्षा में अब एक कंस्ट्रक्टर है जिसे टाइप के एक पैरामीटर की आवश्यकता होती है ICanLog
:
public Database(ICanLog logger) { ... }
अब आपके डेटाबेस में हमेशा उपयोग के लिए एक लकड़हारा होता है, लेकिन यह नहीं जानता कि यह लकड़हारा कहां से आता है।
और यह वह जगह है जहां एक डीआई फ्रेमवर्क चलन में आता है: आप अपने मैपिंग को एक बार फिर से कॉन्फ़िगर करते हैं, और फिर अपने डीआई फ्रेमवर्क को आपके लिए अपने आवेदन को तुरंत करने के लिए कहते हैं। जैसा कि Application
कक्षा को एक ICanPersistData
कार्यान्वयन की आवश्यकता होती है, उदाहरण के Database
लिए इंजेक्ट किया जाता है - लेकिन इसके लिए पहले उस तरह के लकड़हारे का एक उदाहरण बनाना होगा जो इसके लिए कॉन्फ़िगर किया गया है ICanLog
। और इसी तरह ...
इसलिए, एक लंबी कहानी को छोटा करने के लिए: निर्भरता इंजेक्शन आपके कोड में निर्भरता को दूर करने के दो तरीकों में से एक है। यह संकलन-समय के बाद कॉन्फ़िगरेशन परिवर्तन के लिए बहुत उपयोगी है, और यह इकाई परीक्षण के लिए एक बहुत अच्छी बात है (क्योंकि यह स्टब्स और / या मोक्स को इंजेक्ट करना बहुत आसान बनाता है)।
व्यवहार में, ऐसी चीजें हैं जो आप सेवा लोकेटर के बिना नहीं कर सकते हैं (उदाहरण के लिए, यदि आप पहले से नहीं जानते हैं कि आपको एक विशिष्ट इंटरफ़ेस की कितनी आवृत्तियों की आवश्यकता है: एक डीआई फ्रेमवर्क हमेशा प्रति पैरामीटर केवल एक उदाहरण इंजेक्ट करता है, लेकिन आप कॉल कर सकते हैं एक लूप के अंदर एक सेवा लोकेटर, निश्चित रूप से), इसलिए अक्सर प्रत्येक DI फ्रेमवर्क भी एक सेवा लोकेटर प्रदान करता है।
लेकिन मूल रूप से, यह बात है।
पुनश्च: मैंने यहां जो वर्णन किया है, वह कंस्ट्रक्टर इंजेक्शन नामक एक तकनीक है , संपत्ति इंजेक्शन भी है जहां कंस्ट्रक्टर पैरामीटर नहीं हैं, लेकिन गुणों का उपयोग निर्भरता को परिभाषित करने और हल करने के लिए किया जा रहा है। एक वैकल्पिक निर्भरता के रूप में संपत्ति इंजेक्शन के बारे में सोचो, और अनिवार्य निर्भरता के रूप में निर्माता इंजेक्शन। लेकिन इस पर चर्चा इस सवाल के दायरे से बाहर है।