क्या इंजेक्शन लगाने की निर्भरता ctor या प्रति विधि में होनी चाहिए?


16

विचार करें:

public class CtorInjectionExample
{
    public CtorInjectionExample(ISomeRepository SomeRepositoryIn, IOtherRepository OtherRepositoryIn)
    {
        this._someRepository = SomeRepositoryIn;
        this._otherRepository = OtherRepositoryIn;
    }

    public void SomeMethod()
    {
        //use this._someRepository
    }

    public void OtherMethod()
    {
        //use this._otherRepository
    }
}

विरुद्ध:

public class MethodInjectionExample
{
    public MethodInjectionExample()
    {
    }

    public void SomeMethod(ISomeRepository SomeRepositoryIn)
    {
        //use SomeRepositoryIn
    }

    public void OtherMethod(IOtherRepository OtherRepositoryIn)
    {
        //use OtherRepositoryIn
    }
}

जबकि Ctor इंजेक्शन विस्तार को कठिन बनाता है (नए निर्भरता के जुड़ने पर ctor को कॉल करने वाले किसी भी कोड को अपडेट करने की आवश्यकता होगी), और विधि स्तर इंजेक्शन वर्ग स्तर की निर्भरता से अधिक समझाया जाता है और मुझे इन तरीकों के विरुद्ध / के लिए कोई अन्य तर्क नहीं मिल सकता है ।

क्या इंजेक्शन लेने के लिए एक निश्चित दृष्टिकोण है?

(एनबी I ने इस पर जानकारी के लिए खोज की है और इस प्रश्न को उद्देश्य बनाने की कोशिश की है।)


4
यदि आपकी कक्षाएं सामंजस्यपूर्ण हैं, तो कई अलग-अलग तरीकों से एक ही फ़ील्ड का उपयोग किया जाएगा। यह आपको वह उत्तर देना चाहिए जो आप चाहते हैं ...
Oded

@ अच्छा बिंदु, सामंजस्य निर्णय को भारी प्रभावित करेगा। यदि कोई वर्ग है, तो कहो, सहायक विधियों का संग्रह, जिनमें से प्रत्येक में अलग-अलग निर्भरताएं होती हैं (प्रतीत होता है कि सामंजस्यपूर्ण नहीं है, लेकिन तार्किक रूप से समान है) और दूसरा अत्यधिक सामंजस्यपूर्ण है, उन्हें प्रत्येक को सबसे अधिक लाभकारी दृष्टिकोण का उपयोग करना चाहिए। हालांकि, यह एक समाधान के दौरान असंगति का कारण होगा।
स्टुपरयूसर

मेरे द्वारा दिए गए उदाहरणों के लिए प्रासंगिक: en.wikipedia.org/wiki/False_dilemma
StuperUser

जवाबों:


3

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


15

ठीक है, पहले बंद करो, "नए निर्भरता को जोड़ने पर ctor को कॉल करने के लिए अद्यतन करने की आवश्यकता होगी" किसी भी कोड से निपटें; स्पष्ट होने के लिए, यदि आप निर्भरता इंजेक्शन कर रहे हैं और आपके पास निर्भरता वाली किसी वस्तु पर नया () कॉलिंग कोड है, तो आप इसे गलत कर रहे हैं

आपका DI कंटेनर सभी प्रासंगिक निर्भरताओं को इंजेक्ट करने में सक्षम होना चाहिए, इसलिए आपको कंस्ट्रक्टर हस्ताक्षर को बदलने के बारे में चिंता करने की आवश्यकता नहीं है, ताकि तर्क वास्तव में पकड़ में आए।

प्रति-विधि बनाम प्रति-वर्ग इंजेक्शन विचार के लिए, प्रति-विधि इंजेक्शन के साथ दो प्रमुख मुद्दे हैं।

एक मुद्दा यह है कि आपकी कक्षा की विधियाँ निर्भरताएँ साझा करती हैं, यह सुनिश्चित करने में मदद करने का एक तरीका है कि आपकी कक्षाएं प्रभावी रूप से अलग हो जाएं, यदि आप बड़ी संख्या में आश्रितों वाली श्रेणी देखते हैं (संभवतः 4-5 से अधिक) तो वह वर्ग एक प्रमुख उम्मीदवार है दो वर्गों में परावर्तन के लिए।

अगला मुद्दा यह है कि निर्भरता, प्रति-विधि "इंजेक्ट" करने के लिए, आपको उन्हें विधि कॉल में पास करना होगा। इसका मतलब यह है कि आपको विधि कॉल से पहले निर्भरता को हल करना होगा, इसलिए आप इस तरह कोड का एक गुच्छा के साथ समाप्त हो जाएंगे:

var someDependency = ServiceLocator.Resolve<ISomeDependency>();
var something = classBeingInjected.DoStuff(someDependency);

अब, कहते हैं कि आप अपने आवेदन के चारों ओर 10 स्थानों पर इस पद्धति को कॉल करने जा रहे हैं: आपके पास इनमें से 10 स्निपेट्स होंगे। इसके बाद, मान लें कि आपको DoStuff में एक और निर्भरता जोड़ने की आवश्यकता है (): आप उस स्निपेट को 10 बार बदलने जा रहे हैं (या इसे एक विधि में लपेटेंगे, जिस स्थिति में आप बस DI व्यवहार को मैन्युअल रूप से दोहरा रहे हैं, जो एक बेकार है समय की)।

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

इसकी तुलना कंस्ट्रक्टर इंजेक्शन से करें; कंस्ट्रक्टर इंजेक्शन में आप एक विशेष DI कंटेनर से बंधे नहीं हैं, और आप अपनी कक्षाओं की निर्भरता को पूरा करने के लिए सीधे जिम्मेदार नहीं हैं, इसलिए रखरखाव काफी सिरदर्द-मुक्त है।

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

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


1
"if you're doing dependency injection and you have any code calling new() on an object with dependencies, you're doing it wrong."यदि आप किसी वर्ग पर एक विधि का परीक्षण कर रहे हैं, तो आपको उसे तुरंत जांचना होगा या क्या आप DI का उपयोग अपने कोड के तहत परीक्षण को तत्काल करने के लिए कर सकते हैं?
स्टुपरयूसर

@StuperUser Unit का परीक्षण थोड़ा अलग परिदृश्य है, मेरी टिप्पणी केवल उत्पादन कोड पर ही लागू होती है। जैसा कि आप मॉकिंग ढांचे के माध्यम से अपने परीक्षणों के लिए नकली (या ठूंठ) निर्भरता बनाने जा रहे हैं, प्रत्येक पूर्व-कॉन्फ़िगर व्यवहार और मान्यताओं और दावे के सेट के साथ, आपको उन्हें हाथ से इंजेक्ट करना होगा।
एड जेम्स

1
यह वह कोड है जिसके बारे में मैं बात कर रहा हूं "any code calling the ctor will need updating", जब मैं कहता हूं कि मेरा प्रोडक्शन कोड कॉलर्स को नहीं बुलाता है। इन कारणों के लिए।
स्टुपरयूसर

2
@StuperUser मेला पर्याप्त है, लेकिन आप अभी भी आवश्यक निर्भरता के साथ तरीकों को कॉल करने जा रहे हैं, जिसका मतलब है कि मेरे जवाब के बाकी अभी भी खड़े हैं! ईमानदार होने के लिए, मैं एक इकाई परीक्षण के दृष्टिकोण से, निर्माता इंजेक्शन और सभी निर्भरता को मजबूर करने के मुद्दे के बारे में सहमत हूं। मैं संपत्ति इंजेक्शन का उपयोग करने के लिए जाता हूं, क्योंकि यह आपको केवल उन निर्भरताओं को इंजेक्ट करने की अनुमति देता है जिनकी आपको एक परीक्षण के लिए आवश्यकता है, जो मुझे लगता है कि मूल रूप से निहित Assert.WasNotCalled कॉल का एक और सेट है, वास्तव में किसी भी कोड को लिखने के बिना! : D
एड जेम्स

3

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

जवाब है: यह जरूरत पर निर्भर करता है। कभी-कभी एक तरह का उपयोग करना बेहतर होता है, और दूसरा अलग तरह का।

मुझे व्यक्तिगत रूप से कंस्ट्रक्टर इंजेक्टर पसंद हैं, क्योंकि कंस्ट्रक्टर ऑब्जेक्ट को पूरी तरह से इनिशियलाइज़ करने के लिए है। बाद में एक सेटर को कॉल करने से ऑब्जेक्ट पूरी तरह से निर्मित नहीं होता है।


3

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


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

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

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

2
@ क्रिस वास्तव में, सामान्य परिस्थितियों में निर्माणकर्ता और संपत्ति इंजेक्शन बहुत पहचान के साथ काम करते हैं, ऑब्जेक्ट को रिज़ॉल्यूशन के दौरान पूरी तरह से इंजेक्ट किया जाता है। इकाई परीक्षण के दौरान इसे "अमान्य" माना जाने वाला एकमात्र समय है, जब आप कार्यान्वयन को तत्काल करने के लिए DI कंटेनर का उपयोग नहीं करने जा रहे हैं। यह मेरे लिए मेरे उत्तर पर एक टिप्पणी देखें कि यह वास्तव में क्यों फायदेमंद है। मैं सराहना करता हूं कि परिवर्तनशीलता एक मुद्दा हो सकता है, लेकिन वास्तविक रूप से आप केवल अपने इंटरफेस के माध्यम से हल की गई कक्षाओं को संदर्भित करने जा रहे हैं, जो संपादित करने के लिए निर्भरता को उजागर नहीं करेंगे।
एड जेम्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.