एक निर्भरता इंजेक्शन का उपयोग क्यों करता है?


536

मैं निर्भरता इंजेक्शन (DI) को समझने की कोशिश कर रहा हूं , और एक बार फिर मैं असफल रहा। यह सिर्फ मूर्खतापूर्ण लगता है। मेरा कोड कभी गड़बड़ नहीं है; मैं शायद ही वर्चुअल फ़ंक्शंस और इंटरफेस लिखता हूं (हालांकि मैं एक बार नीला चाँद में करता हूं) और मेरे सभी कॉन्फ़िगरेशन को जादुई रूप से json.net (कभी-कभी XML सीरियलाइज़र का उपयोग करके) का उपयोग करके एक वर्ग में क्रमबद्ध किया जाता है।

मुझे समझ में नहीं आता कि यह किस समस्या का हल करता है। यह कहने का एक तरीका है: "जब आप इस फ़ंक्शन में भाग लेते हैं, तो एक ऐसी वस्तु लौटाते हैं जो इस प्रकार की होती है और इन मापदंडों या डेटा का उपयोग करती है।"
लेकिन ... मैं कभी क्यों इस्तेमाल करूंगा? ध्यान दें कि मुझे कभी भी उपयोग करने की आवश्यकता नहीं है object, लेकिन मैं समझता हूं कि इसके लिए क्या है।

किसी वेबसाइट या डेस्कटॉप एप्लिकेशन के निर्माण में कुछ वास्तविक स्थितियां क्या होती हैं, जहां कोई DI का उपयोग करेगा? मैं आसानी से मामलों के साथ आ सकता हूं कि कोई गेम में इंटरफेस / वर्चुअल फ़ंक्शंस का उपयोग क्यों करना चाहता है, लेकिन यह बहुत ही दुर्लभ (दुर्लभ है कि मैं एक भी उदाहरण याद नहीं कर सकता) गैर-गेम कोड का उपयोग करने के लिए।


3
यह भी उपयोगी जानकारी हो सकती है: martinfowler.com/articles/injection.html
ta.speot.is



41
मैं इस लड़के के साथ हूं: jamesshore.com/Blog/D dependency-Injection-Demystified.html
एरिक लिपर्ट

5
DI का एक और बहुत ही सरल विवरण: codearsenal.net/2015/03/…
ybonda

जवाबों:


840

सबसे पहले, मैं इस उत्तर के लिए एक धारणा की व्याख्या करना चाहता हूं। यह हमेशा सच नहीं होता है, लेकिन अक्सर:

इंटरफेस विशेषण हैं; कक्षाएं संज्ञा हैं।

(वास्तव में, ऐसे इंटरफेस हैं जो संज्ञा भी हैं, लेकिन मैं यहां सामान्य करना चाहता हूं।)

इसलिए, उदाहरण के लिए एक इंटरफ़ेस कुछ इस तरह के 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 फ्रेमवर्क भी एक सेवा लोकेटर प्रदान करता है।

लेकिन मूल रूप से, यह बात है।

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


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

136
यहाँ वह चीज़ है जो मुझे DI के बारे में कभी नहीं मिली: यह वास्तुकला को बहुत अधिक जटिल बनाता है। और फिर भी, जैसा कि मैं इसे देखता हूं, उपयोग बहुत सीमित है। उदाहरण निश्चित रूप से हमेशा समान होते हैं: विनिमेय लॉगर, विनिमेय मॉडल / डेटा एक्सेस। कभी-कभी विनिमेय दृश्य। लेकिन इतना ही। क्या ये कुछ मामले वास्तव में एक अधिक जटिल सॉफ्टवेयर आर्किटेक्चर को सही ठहराते हैं? - पूर्ण प्रकटीकरण: मैंने पहले से ही डीआई को बहुत प्रभाव के लिए इस्तेमाल किया है, लेकिन यह एक बहुत ही विशेष प्लग-इन वास्तुकला के लिए था जिसे मैं सामान्य नहीं करूंगा।
कोनराड रुडोल्फ

17
@GoloRoden, आप ILogger के बजाय इंटरफ़ेस ICanLog क्यों कह रहे हैं? मैंने एक और प्रोग्रामर के साथ काम किया, जो अक्सर ऐसा करता था, और मैं कभी भी सम्मेलन को समझ नहीं पाया? मेरे लिए यह IEnumerable ICanEnumerate कॉल करने जैसा है?
DermFrench 16

28
मैंने इसे ICanLog कहा, क्योंकि हम अक्सर शब्दों (संज्ञा) के साथ काम करते हैं जिसका मतलब कुछ भी नहीं है। जैसे, ब्रोकर क्या है? प्रबंधक? यहां तक ​​कि एक रिपॉजिटरी को एक अनोखे तरीके से परिभाषित नहीं किया गया है। और इन सभी चीजों को संज्ञा के रूप में रखना ओओ भाषाओं की एक विशिष्ट बीमारी है (देखें steve-yegge.blogspot.de/2006/03/… )। मैं जो व्यक्त करना चाहता हूं वह यह है कि मेरे पास एक घटक है जो मेरे लिए लॉगिंग कर सकता है - इसलिए इसे इस तरह से क्यों नहीं बुलाया जाए? बेशक, यह भी प्रथम व्यक्ति के रूप में I के साथ खेल रहा है, इसलिए ICanLog (ForYou)।
गोलो रॉडेन

18
@ डेविड यूनिट का परीक्षण ठीक काम करता है - आखिरकार, एक इकाई अन्य चीजों से स्वतंत्र है (अन्यथा यह एक इकाई नहीं है)। DI कंटेनरों के बिना क्या काम नहीं है परीक्षण नकली है। पर्याप्त रूप से, मैं इस बात से सहमत नहीं हूं कि सभी मामलों में डीआई कंटेनरों को जोड़ने की जटिल जटिलता का फायदा होगा। मैं कठोर इकाई परीक्षण करता हूं। मैं शायद ही कभी मॉकिंग करता हूं।
कोनराड रुडोल्फ

499

मुझे लगता है कि बहुत बार लोग निर्भरता इंजेक्शन और एक निर्भरता इंजेक्शन ढांचे (या एक कंटेनर के रूप में इसे अक्सर कहा जाता है) के बीच अंतर के बारे में भ्रमित हो जाते हैं ।

निर्भरता इंजेक्शन एक बहुत ही सरल अवधारणा है। इस कोड के बजाय:

public class A {
  private B b;

  public A() {
    this.b = new B(); // A *depends on* B
  }

  public void DoSomeStuff() {
    // Do something with B here
  }
}

public static void Main(string[] args) {
  A a = new A();
  a.DoSomeStuff();
}

आप इस तरह कोड लिखते हैं:

public class A {
  private B b;

  public A(B b) { // A now takes its dependencies as arguments
    this.b = b; // look ma, no "new"!
  }

  public void DoSomeStuff() {
    // Do something with B here
  }
}

public static void Main(string[] args) {
  B b = new B(); // B is constructed here instead
  A a = new A(b);
  a.DoSomeStuff();
}

और बस। गंभीरता से। इससे आपको एक टन लाभ मिलता है। दो महत्वपूर्ण चीजें Main()आपके कार्यक्रम में फैलने के बजाय एक केंद्रीय स्थान ( फ़ंक्शन) से कार्यक्षमता को नियंत्रित करने की क्षमता हैं , और अलगाव में प्रत्येक वर्ग को अधिक आसानी से परीक्षण करने की क्षमता है (क्योंकि आप इसके निर्माण में नकली या अन्य नकली वस्तुओं को पारित कर सकते हैं। एक वास्तविक मूल्य की)।

बेशक, यह है कि अब आपके पास एक मेगा-फ़ंक्शन है जो आपके प्रोग्राम द्वारा उपयोग किए जाने वाले सभी वर्गों के बारे में जानता है। यही डीआई फ्रेमवर्क में मदद कर सकता है। लेकिन अगर आपको यह समझने में परेशानी हो रही है कि यह दृष्टिकोण क्यों मूल्यवान है, तो मैं पहले मैनुअल निर्भरता इंजेक्शन के साथ शुरू करने की सलाह दूंगा, इसलिए आप बेहतर तरीके से सराहना कर सकते हैं कि आपके लिए विभिन्न रूपरेखाएं क्या कर सकती हैं।


7
मैं पहले वाले के बजाय दूसरे कोड को क्यों पसंद करूंगा? पहले वाले के पास केवल एक नया कीवर्ड है, यह कैसे मदद करता है?
user962206

17
@ user962206 इस बारे में सोचें कि आप B

66
@ user962206, यह भी सोचें कि अगर बी को इसके निर्माता में कुछ मापदंडों की आवश्यकता है तो क्या होगा: इसे तुरंत करने के लिए, ए को उन मापदंडों के बारे में जानना होगा, कुछ ऐसा जो ए से पूरी तरह से असंबंधित हो सकता है (यह सिर्फ बी पर निर्भर करना चाहता है। नहीं, जो बी पर निर्भर करता है)। एक पहले से निर्मित बी (या उस विषय के लिए बी के किसी भी उपवर्ग या मॉक) को पास करना, ए के निर्माता को हल करता है जो ए बनाता है और ए केवल बी पर निर्भर करता है
महामारी

17
@ acidzombie24: कई डिज़ाइन पैटर्न की तरह, DI वास्तव में तब तक उपयोगी नहीं होता जब तक कि आपका कोडबेस एक समस्या बनने के लिए सरल दृष्टिकोण के लिए पर्याप्त नहीं होता। मेरी आंत की भावना यह है कि DI वास्तव में एक सुधार नहीं होगा जब तक कि आपके आवेदन में कोड की लगभग 20,000 से अधिक लाइनें हों, और / या अन्य पुस्तकालयों या चौखटे पर 20 से अधिक निर्भरता हो। यदि आपका एप्लिकेशन इससे छोटा है, तो आप अभी भी DI शैली में प्रोग्राम करना पसंद कर सकते हैं, लेकिन अंतर लगभग नाटकीय नहीं होगा।
डैनियल प्राइडेन

2
@DanielPryden मुझे नहीं लगता कि कोड आकार उतना ही मायने रखता है जितना कि आपका कोड कितना गतिशील है। यदि आप नियमित रूप से नए इंटरफ़ेस जोड़ रहे हैं जो समान इंटरफ़ेस में फिट होते हैं, तो आपको अक्सर निर्भर कोड को बदलना नहीं पड़ेगा।
FistOfFury

35

जैसा कि अन्य उत्तर में कहा गया है, निर्भरता इंजेक्शन वर्ग के बाहर आपकी निर्भरता बनाने का एक तरीका है जो इसका उपयोग करता है। आप उन्हें बाहर से इंजेक्ट करते हैं, और अपनी रचना के बारे में अपनी कक्षा के अंदर से नियंत्रित करते हैं। यह भी क्यों निर्भरता इंजेक्शन नियंत्रण के उलट का एक एहसास है (IoC) सिद्धांत ।

IoC सिद्धांत है, जहां DI पैटर्न है। कारण यह है कि आपको "एक से अधिक लकड़हारे की आवश्यकता है" वास्तव में कभी नहीं मिल सकता है, जहां तक ​​मेरा अनुभव जाता है, लेकिन वास्तविक कारण यह है, कि आपको वास्तव में इसकी आवश्यकता है, जब भी आप कुछ परीक्षण करते हैं। एक उदाहरण:

मेरी सुविधा:

जब मैं एक प्रस्ताव को देखता हूं, तो मैं यह चिह्नित करना चाहता हूं कि मैंने इसे स्वचालित रूप से देखा, ताकि मैं ऐसा करना न भूलें।

आप इसे इस तरह से परख सकते हैं:

[Test]
public void ShouldUpdateTimeStamp
{
    // Arrange
    var formdata = { . . . }

    // System under Test
    var weasel = new OfferWeasel();

    // Act
    var offer = weasel.Create(formdata)

    // Assert
    offer.LastUpdated.Should().Be(new DateTime(2013,01,13,13,01,0,0));
}

तो कहीं न कहीं OfferWeasel, यह आपको इस तरह एक प्रस्ताव वस्तु बनाता है:

public class OfferWeasel
{
    public Offer Create(Formdata formdata)
    {
        var offer = new Offer();
        offer.LastUpdated = DateTime.Now;
        return offer;
    }
}

यहाँ समस्या यह है, कि यह परीक्षण हमेशा होने की संभावना हमेशा विफल रहेगी, क्योंकि जो तिथि निर्धारित की जा रही है, वह उस तिथि से भिन्न होगी, भले ही आपने DateTime.Nowपरीक्षण कोड में डाल दिया हो, यह कुछ मिलीसेकंड से दूर हो सकता है और इसलिए हमेशा असफल। एक बेहतर समाधान अब इसके लिए एक इंटरफ़ेस बनाना होगा, जो आपको यह नियंत्रित करने की अनुमति देगा कि किस समय सेट किया जाएगा:

public interface IGotTheTime
{
    DateTime Now {get;}
}

public class CannedTime : IGotTheTime
{
    public DateTime Now {get; set;}
}

public class ActualTime : IGotTheTime
{
    public DateTime Now {get { return DateTime.Now; }}
}

public class OfferWeasel
{
    private readonly IGotTheTime _time;

    public OfferWeasel(IGotTheTime time)
    {
        _time = time;
    }

    public Offer Create(Formdata formdata)
    {
        var offer = new Offer();
        offer.LastUpdated = _time.Now;
        return offer;
    }
}

इंटरफ़ेस अमूर्त है। एक असली बात है, और दूसरा आपको कुछ समय नकली करने की अनुमति देता है जहां इसकी आवश्यकता होती है। परीक्षण को इस तरह बदला जा सकता है:

[Test]
public void ShouldUpdateTimeStamp
{
    // Arrange
    var date = new DateTime(2013, 01, 13, 13, 01, 0, 0);
    var formdata = { . . . }

    var time = new CannedTime { Now = date };

    // System under test
    var weasel= new OfferWeasel(time);

    // Act
    var offer = weasel.Create(formdata)

    // Assert
    offer.LastUpdated.Should().Be(date);
}

इस तरह, आपने "नियंत्रण के व्युत्क्रम" सिद्धांत को लागू किया, एक निर्भरता को इंजेक्ट करके (वर्तमान समय में)। ऐसा करने का मुख्य कारण आसान पृथक इकाई परीक्षण के लिए है, इसे करने के अन्य तरीके हैं। उदाहरण के लिए, यहां एक इंटरफ़ेस और एक वर्ग अनावश्यक है क्योंकि C # फ़ंक्शन को चर के रूप में पास किया जा सकता है, इसलिए इंटरफ़ेस के बजाय आप Func<DateTime>इसे प्राप्त करने के लिए उपयोग कर सकते हैं । या, यदि आप एक गतिशील दृष्टिकोण लेते हैं, तो आप किसी भी ऑब्जेक्ट को पास करते हैं जिसमें समतुल्य विधि ( बतख टाइपिंग) है ) होती है, और आपको एक इंटरफ़ेस की आवश्यकता नहीं होती है।

आपको शायद ही कभी एक से अधिक लकड़हारे की आवश्यकता होगी। बहरहाल, सांख्यिकीय रूप से टाइप किए गए कोड जैसे जावा या C # के लिए निर्भरता इंजेक्शन आवश्यक है।

तथा... यह भी ध्यान दिया जाना चाहिए कि कोई वस्तु केवल रनटाइम पर अपने उद्देश्य को ठीक से पूरा कर सकती है, अगर इसकी सभी निर्भरताएं उपलब्ध हैं, इसलिए संपत्ति इंजेक्शन स्थापित करने में बहुत अधिक उपयोग नहीं है। मेरी राय में, सभी आश्रितों को संतुष्ट होना चाहिए जब कंस्ट्रक्टर को बुलाया जा रहा है, इसलिए कंस्ट्रक्टर-इंजेक्शन के साथ जाने की बात है।

मुझे उम्मीद है कि इससे मदद मिली।


4
यह वास्तव में एक भयानक समाधान की तरह दिखता है। मैं निश्चित रूप से कोड अधिक लिखूंगा जैसे डैनियल प्राइडेन का सुझाव है, लेकिन उस विशिष्ट इकाई परीक्षण के लिए मैं सिर्फ DateTime.Now कार्य से पहले और बाद में जांच करूंगा कि क्या बीच में समय है? कोड के अधिक इंटरफेस / बहुत अधिक लाइनें जोड़ने से मुझे एक खराब विचार जैसा लगता है।

3
मुझे जेनेरिक ए (बी) के उदाहरण पसंद नहीं हैं और मुझे कभी नहीं लगा कि 100 कार्यान्वयन के लिए एक लकड़हारे की आवश्यकता होती है। यह एक उदाहरण है जो मैंने हाल ही में सामना किया है और इसे हल करने के 5 तरीकों में से एक है, जहां एक वास्तव में पोस्टशेयर का उपयोग करना शामिल है। यह एक क्लासिक वर्ग आधारित ctor इंजेक्शन दृष्टिकोण को दिखाता है। क्या आप एक बेहतर वास्तविक दुनिया उदाहरण प्रदान कर सकते हैं जहां आपको DI के लिए अच्छा उपयोग मिला है?
cessor

2
मैंने कभी भी DI के लिए एक अच्छा उपयोग नहीं देखा। इसीलिए मैंने सवाल लिखा।

2
मुझे यह मददगार नहीं लगा। मेरा कोड टेस्ट करना हमेशा आसान होता है। ऐसा प्रतीत होता है कि DI खराब कोड वाले बड़े कोड बेस के लिए अच्छा है।

1
ज्ञात हो कि छोटे कार्यात्मक कार्यक्रमों में भी, जब भी आप f (x), g (f) आप पहले से निर्भरता इंजेक्शन का उपयोग कर रहे हैं, तो JS में प्रत्येक निरंतरता एक निर्भरता इंजेक्शन के रूप में गिना जाएगा। ); मेरा अनुमान है कि आप पहले से ही उपयोग कर रहे हैं है
cessor

15

मुझे लगता है कि क्लासिक उत्तर एक अधिक डिकॉउडेड एप्लिकेशन बनाने के लिए है, जिसके बारे में कोई जानकारी नहीं है कि रनटाइम के दौरान किस कार्यान्वयन का उपयोग किया जाएगा।

उदाहरण के लिए, हम एक केंद्रीय भुगतान प्रदाता हैं, जो दुनिया भर के कई भुगतान प्रदाताओं के साथ काम कर रहे हैं। हालांकि, जब कोई अनुरोध किया जाता है, तो मुझे पता नहीं है कि मैं किस भुगतान प्रोसेसर को कॉल करने जा रहा हूं। मैं स्विच के मामलों की एक टन के साथ एक वर्ग कार्यक्रम कर सकता है, जैसे:

class PaymentProcessor{

    private String type;

    public PaymentProcessor(String type){
        this.type = type;
    }

    public void authorize(){
        if (type.equals(Consts.PAYPAL)){
            // Do this;
        }
        else if(type.equals(Consts.OTHER_PROCESSOR)){
            // Do that;
        }
    }
}

अब कल्पना करें कि अब आपको एक ही वर्ग में इस सभी कोड को बनाए रखने की आवश्यकता होगी क्योंकि यह ठीक से डिकोड नहीं किया गया है, तो आप कल्पना कर सकते हैं कि आपके द्वारा उपयोग किए जाने वाले प्रत्येक नए प्रोसेसर के लिए, आपको एक नया / // केस स्विच करने की आवश्यकता होगी। हर विधि, यह केवल अधिक जटिल हो जाती है, हालांकि, डिपेंडेंसी इंजेक्शन (या नियंत्रण का उलटा) का उपयोग करके - जैसा कि कभी-कभी कहा जाता है, जिसका अर्थ है कि जो कोई भी प्रोग्राम को चलाने के नियंत्रण को केवल रनटाइम पर जाना जाता है, और जटिलता नहीं है), आप कुछ हासिल कर सकते हैं बहुत साफ और बनाए रखने योग्य।

class PaypalProcessor implements PaymentProcessor{

    public void authorize(){
        // Do PayPal authorization
    }
}

class OtherProcessor implements PaymentProcessor{

    public void authorize(){
        // Do other processor authorization
    }
}

class PaymentFactory{

    public static PaymentProcessor create(String type){

        switch(type){
            case Consts.PAYPAL;
                return new PaypalProcessor();

            case Consts.OTHER_PROCESSOR;
                return new OtherProcessor();
        }
    }
}

interface PaymentProcessor{
    void authorize();
}

** कोड संकलन नहीं होगा, मुझे पता है :)


+1 क्योंकि यह लगता है कि आप कह रहे हैं कि आपको इसकी आवश्यकता है जहाँ आप आभासी विधियों / इंटरफेस का उपयोग करते हैं। लेकिन यह अभी भी दुर्लभ है। मैं अभी भी इसे एक ही समय में पारित कर new ThatProcessor()

@ इट्स आप क्लास फैक्टरी डिजाइन पैटर्न के साथ अनगिनत स्विच से बच सकते हैं। उपयोग प्रतिबिंब System.Reflection.Assembly.GetExecutingAssembly () CreateInstance ()।
domenicr

@domenicr का संभोग! लेकिन मैं इसे एक सरल उदाहरण में समझाना चाहता था
इटाई सगी

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

उदाहरण के लिए (c ++ के अनुसार) हमारे पास एक सामान्य इंटरफ़ेस है जो बस बेस क्लास के संदर्भ को लेता है और चयन के बिना इसके व्युत्पन्न वर्ग के व्यवहार को लागू करता है। शून्य धुन (इंस्ट्रूमेंट & आई) {i.play (मिडिल); } int main () {विंड फ्लूट; धुन (बांसुरी); } इंस्ट्रूमेंट बेस क्लास है, इससे हवा निकलती है। सी ++ के अनुसार, वर्चुअल फ़ंक्शन सामान्य इंटरफ़ेस के माध्यम से व्युत्पन्न वर्ग के व्यवहार को लागू करना संभव बनाता है।
अरविन्द क्रमर

6

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

लंबे समय तक ओओपी में यह पहले से ही आम है। कई बार कोड के टुकड़े बनाने जैसे:

I_Dosomething x = new Impl_Dosomething();

दोष यह है कि कार्यान्वयन वर्ग अभी भी हार्डकोडेड है, इसलिए सामने वाला ज्ञान है जो कार्यान्वयन का उपयोग किया जाता है। DI इंटरफ़ेस को एक कदम आगे ले जाता है, कि सामने वाले को जानने के लिए एकमात्र चीज़ इंटरफ़ेस का ज्ञान है। DYI और DI के बीच सेवा लोकेटर का पैटर्न है, क्योंकि सामने के छोर को एक कुंजी (सेवा लोकेटर की रजिस्ट्री में मौजूद) प्रदान करनी होती है ताकि उसका अनुरोध हल हो सके। सेवा लोकेटर उदाहरण:

I_Dosomething x = ServiceLocator.returnDoing(String pKey);

DI उदाहरण:

I_Dosomething x = DIContainer.returnThat();

डि की आवश्यकताओं में से एक यह है कि कंटेनर को यह पता लगाने में सक्षम होना चाहिए कि कौन सा वर्ग किस इंटरफ़ेस का कार्यान्वयन है। इसलिए डीआई कंटेनर को दृढ़ता से टाइप किए गए डिज़ाइन की आवश्यकता होती है और एक ही समय में प्रत्येक इंटरफ़ेस के लिए केवल एक कार्यान्वयन की आवश्यकता होती है। यदि आपको एक ही समय (एक कैलकुलेटर की तरह) एक इंटरफ़ेस के अधिक कार्यान्वयन की आवश्यकता है, तो आपको सेवा लोकेटर या फ़ैक्टरी डिज़ाइन पैटर्न की आवश्यकता है।

डी (बी) मैं: निर्भरता इंजेक्शन और डिजाइन इंटरफेस द्वारा। यह प्रतिबंध हालांकि एक बहुत बड़ी व्यावहारिक समस्या नहीं है। D (b) I का उपयोग करने का लाभ यह है कि यह क्लाइंट और प्रदाता के बीच संचार का कार्य करता है। एक इंटरफ़ेस एक वस्तु या व्यवहार के एक सेट पर एक परिप्रेक्ष्य है। उत्तरार्द्ध यहाँ महत्वपूर्ण है।

मैं कोडिंग में डी (बी) I के साथ सेवा अनुबंधों के प्रशासन को प्राथमिकता देता हूं। उन्हें एक साथ जाना चाहिए। सेवा अनुबंधों के संगठनात्मक प्रशासन के बिना तकनीकी समाधान के रूप में डी (बी) I का उपयोग मेरे दृष्टिकोण से बहुत फायदेमंद नहीं है, क्योंकि DI तब केवल एनकैप्सुलेशन की एक अतिरिक्त परत है। लेकिन जब आप इसे संगठनात्मक प्रशासन के साथ मिलकर उपयोग कर सकते हैं तो आप वास्तव में आयोजन सिद्धांत D (b) I प्रदान करते हैं। यह आपको परीक्षण और संस्करण के विकास और विकल्पों के विकास जैसे विषयों में क्लाइंट और अन्य तकनीकी विभागों के साथ संरचना संचार में लंबे समय तक मदद कर सकता है। जब आपके पास हार्डकोड कोड के रूप में एक अंतर्निहित इंटरफ़ेस होता है, तो यह समय के साथ बहुत कम संचारी होता है तब जब आप डी (बी) I का उपयोग करके स्पष्ट करते हैं। यह सभी रखरखाव के लिए उबलता है, जो समय के साथ होता है और एक समय में नहीं। :-)


1
"दोष यह है कि कार्यान्वयन वर्ग अभी भी हार्डकोडेड है" <- ज्यादातर समय केवल एक ही कार्यान्वयन होता है और जैसे मैंने कहा कि मैं ऐसे नाम कोड के बारे में नहीं सोच सकता जिसके लिए पहले से निर्मित इंटरफ़ेस की आवश्यकता नहीं है (.NET )।

@ acidzombie24 हो सकता है ... लेकिन अगर आप इंटरफेस की जरूरत है, तो बाद में गैर-डीआई समाधान को बदलने के प्रयास में डीआई का उपयोग करके समाधान को लागू करने के प्रयास की तुलना करें। मैं लगभग हमेशा पहले विकल्प के साथ जाता हूँ। कल 100.000 $ का भुगतान करने के बजाय 100 डॉलर का भुगतान करना बेहतर है।
गोलो रॉडेन

1
@GoloRoden वास्तव में, रखरखाव डी (बी) I जैसी तकनीकों का उपयोग करके महत्वपूर्ण मुद्दा है। यह एक आवेदन की लागत का 80% है। एक डिज़ाइन जिसमें आवश्यक व्यवहार को शुरू से इंटरफेस का उपयोग करके स्पष्ट किया जाता है, संगठन को बाद में बहुत समय और धन बचाता है।
लूक बर्गमैन

मैं सही मायने में तब तक नहीं समझूंगा जब तक मुझे इसका भुगतान नहीं करना होगा क्योंकि अभी तक मैंने $ 0 का भुगतान किया था और अब तक मुझे केवल $ 0 का भुगतान करने की आवश्यकता है। लेकिन मैं हर लाइन या फंक्शन को साफ रखने के लिए $ 0.05 का भुगतान करता हूं।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.