एनकैप्सुलेशन की कीमत पर डिपेंडेंसी इंजेक्शन आना चाहिए?


128

अगर मैं सही तरीके से समझूं, तो डिपेंडेंसी इंजेक्शन के लिए विशिष्ट तंत्र एक वर्ग के कंस्ट्रक्टर के माध्यम से या कक्षा के सार्वजनिक संपत्ति (सदस्य) के माध्यम से इंजेक्ट करना है।

यह निर्भरता को इंजेक्शन के रूप में उजागर करता है और इनकैप्सुलेशन के ओओपी सिद्धांत का उल्लंघन करता है।

क्या मैं इस व्यापार की पहचान करने में सही हूं? आप इस मुद्दे से कैसे निपटेंगे?

कृपया नीचे दिए गए मेरे प्रश्न का उत्तर भी देखें।


7
यह एक बहुत ही स्मार्ट प्रश्न imho है
dfa

5
इस प्रश्न का उत्तर देने के लिए पहले एक तर्क की आवश्यकता है कि इनकैप्सुलेशन का क्या अर्थ है। ;)
जेफ सनाट

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

जवाबों:


62

इस मुद्दे को देखने का एक और तरीका है जो आपको दिलचस्प लग सकता है।

जब हम IoC / निर्भरता इंजेक्शन का उपयोग करते हैं, तो हम OOP अवधारणाओं का उपयोग नहीं कर रहे हैं। माना जाता है कि हम 'होस्ट' के रूप में एक OO भाषा का उपयोग कर रहे हैं, लेकिन IoC के पीछे के विचार OO नहीं, बल्कि घटक-उन्मुख सॉफ़्टवेयर इंजीनियरिंग से आते हैं।

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

IoO के माध्यम से हमारे OO कार्यक्रमों में समान तकनीकों को लागू करके, हम प्रोग्राम को कॉन्फ़िगर करने और बनाए रखने के लिए आसान बनाने का लक्ष्य रखते हैं। प्रकाशन निर्भरताएँ (कंस्ट्रक्टर मापदंडों या जो भी हो) इसका एक महत्वपूर्ण हिस्सा है। एनकैप्सुलेशन वास्तव में लागू नहीं होता है, जैसा कि घटक / सेवा उन्मुख दुनिया में है, विवरण से रिसाव के लिए कोई 'कार्यान्वयन प्रकार' नहीं है।

दुर्भाग्य से हमारी भाषाएं वर्तमान में मोटे-अनाज वाले घटक-उन्मुख लोगों से ठीक-ठीक, वस्तु-उन्मुख अवधारणाओं को अलग नहीं करती हैं, इसलिए यह एक अंतर है जिसे आपको केवल अपने दिमाग में रखना है :)


18
एनकैप्सुलेशन केवल फैंसी शब्दावली का एक टुकड़ा नहीं है। यह वास्तविक लाभों के साथ एक वास्तविक बात है, और इससे कोई फर्क नहीं पड़ता कि आप अपने कार्यक्रम को "घटक उन्मुख" या "वस्तु उन्मुख" मानते हैं। एन्कैप्सुलेशन को आपकी वस्तु / घटक / सेवा की स्थिति की रक्षा करने के लिए माना जाता है / जो भी अप्रत्याशित तरीकों से बदला जा रहा है, और IoC इस सुरक्षा को दूर करता है, इसलिए निश्चित रूप से एक व्यापार है।
रॉन इनबार

1
एक कंस्ट्रक्टर के माध्यम से आपूर्ति किए गए तर्क अभी भी अपेक्षित तरीकों के दायरे में आते हैं जिसमें वस्तु को "परिवर्तित" किया जा सकता है: वे स्पष्ट रूप से उजागर होते हैं, और उनके आसपास के आक्रमणकारियों को लागू किया जाता है। सूचना छिपाना उस गोपनीयता के प्रकार के लिए बेहतर शब्द है जिसका आप @RonInbar पर उल्लेख कर रहे हैं, और यह जरूरी नहीं है कि यह हमेशा लाभदायक हो (यह पास्ता को असंगत बनाने के लिए कठिन बनाता है;;)।
निकोलस ब्लमहार्ट

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

1
कोई फर्क नहीं पड़ता कि मैं इसे किस तरह से देखता हूं, यह मुझे लगता है कि डीआई गंभीरता से ओओपी के सबसे मूल्यवान लाभों में से कुछ को कम कर देता है, और मुझे अभी तक ऐसी स्थिति का सामना करना पड़ा है जहां मैंने वास्तव में इसे एक तरीके से उपयोग किया है जो एक वास्तविक हल किया है मौजूदा समस्या।
न्यूट्रिनो

1
यहां समस्या को फ्रेम करने का एक और तरीका है: कल्पना करें कि यदि .NET असेंबली ने "एनकैप्सुलेशन" चुना और यह घोषित नहीं किया कि वे अन्य असेंबली पर किस तरह से भरोसा करते हैं। यह एक पागल स्थिति होगी, डॉक्स पढ़ना और बस यह उम्मीद करना कि कुछ इसे लोड करने के बाद काम करता है। उस स्तर पर निर्भरता की घोषणा करने से ऐप की बड़े पैमाने पर रचना के साथ स्वचालित टूलिंग सौदा होता है। आपको सादृश्य देखने के लिए स्क्विंट करना होगा, लेकिन समान बल घटक स्तर पर लागू होते हैं। हमेशा की तरह ट्रेड-ऑफ और YMMV हैं :-)
निकोलस ब्लमहार्ट

29

यह एक अच्छा सवाल है - लेकिन कुछ बिंदु पर, अपने शुद्ध रूप में कैप्सूलीकरण जरूरतों अगर वस्तु कभी अपनी निर्भरता पूरा किया है करने के लिए है का उल्लंघन किया जाना है। निर्भरता के कुछ प्रदाता को दोनों को पता होना चाहिए कि प्रश्न में वस्तु की आवश्यकता है Foo, और प्रदाता के पास Fooवस्तु को प्रदान करने का एक तरीका है ।

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

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

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

यह निश्चित रूप से एक राय है, लेकिन मेरे दिमाग में डीआई आवश्यक रूप से इनकैप्सुलेशन का उल्लंघन नहीं करता है और वास्तव में आंतरिक के सभी आवश्यक ज्ञान को एक स्थान पर केंद्रीकृत करके इसकी मदद कर सकता है। न केवल यह अपने आप में एक अच्छी बात है, बल्कि इससे भी बेहतर यह जगह आपके खुद के कोडबेस के बाहर है, इसलिए आपके द्वारा लिखे गए किसी भी कोड को कक्षाओं की निर्भरता के बारे में जानने की आवश्यकता नहीं है।


2
अच्छे अंक। मैं निजी क्षेत्रों पर @Autowired का उपयोग करने के खिलाफ सलाह देता हूं; यह परीक्षा के लिए कक्षा को कठिन बनाता है; फिर आप मोक्स या स्टब्स को कैसे इंजेक्ट करते हैं?
lumpynose

4
मैं असहमत हूं। DI एनकैप्सुलेशन का उल्लंघन करता है, और इससे बचा जा सकता है। उदाहरण के लिए, ServiceLocator का उपयोग करके, जिसे स्पष्ट रूप से ग्राहक वर्ग के बारे में कुछ भी जानने की आवश्यकता नहीं है; यह केवल फू निर्भरता के कार्यान्वयन के बारे में जानने की जरूरत है। लेकिन सबसे अच्छी बात, ज्यादातर मामलों में, बस "नए" ऑपरेटर का उपयोग करना है।
रोजेरियो

5
@Rogerio - संभवतः कोई DI ढांचा आपके द्वारा वर्णित ServiceLocator की तरह कार्य करता है; ग्राहक फू कार्यान्वयन के बारे में कुछ विशेष नहीं जानता है, और DI उपकरण ग्राहक के बारे में कुछ भी नहीं जानता है। और "नए" का उपयोग करना एनकैप्सुलेशन का उल्लंघन करने के लिए बहुत बुरा है, क्योंकि आपको न केवल सटीक कार्यान्वयन वर्ग, बल्कि सभी वर्गों और इसके लिए आवश्यक सभी निर्भरता के उदाहरणों को जानने की आवश्यकता है।
आंद्रेज डोयले

4
एक सहायक वर्ग को त्वरित करने के लिए "नया" का उपयोग करना, जो अक्सर सार्वजनिक नहीं होता है, एनकैप्सुलेशन को बढ़ावा देता है। DI का विकल्प सहायक वर्ग को सार्वजनिक करना और ग्राहक वर्ग में सार्वजनिक निर्माणकर्ता या सेटर जोड़ना होगा; दोनों परिवर्तन मूल सहायक वर्ग द्वारा प्रदान किए गए एनकैप्सुलेशन को तोड़ देंगे।
रोगियो

1
"यह एक अच्छा सवाल है - लेकिन कुछ बिंदु पर, इसके शुद्धतम रूप में इनकैप्सुलेशन का उल्लंघन किया जाना चाहिए, यदि वस्तु कभी भी अपनी निर्भरता को पूरा करने के लिए है" तो आपके उत्तर का संस्थापक आधार बस असत्य है। जैसा कि @ Rogério आंतरिक रूप से एक निर्भरता को नया रूप देता है, और कोई अन्य विधि जहां कोई वस्तु आंतरिक रूप से अपनी निर्भरता को पूरा करती है, वह एनकैप्सुलेशन का उल्लंघन नहीं करती है।
न्यूट्रिनो

17

यह निर्भरता को इंजेक्शन के रूप में उजागर करता है और इनकैप्सुलेशन के ओओपी सिद्धांत का उल्लंघन करता है।

खैर, स्पष्ट रूप से, सब कुछ एनकैप्सुलेशन का उल्लंघन करता है। :) यह एक प्रकार का निविदा सिद्धांत है जिसे अच्छी तरह से व्यवहार किया जाना चाहिए।

तो, इनकैप्सुलेशन का उल्लंघन क्या है?

वंशानुक्रम करता है

"क्योंकि वंशानुक्रम अपने माता-पिता के कार्यान्वयन के विवरण के लिए एक उपवर्ग को उजागर करता है, इसलिए यह अक्सर कहा जाता है कि 'विरासत में अतिक्रमण टूटता है"। (गैंग ऑफ़ फोर 1995: 19)

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

सी ++ में दोस्त घोषणा करता है

रूबी में क्लास एक्सटेन्शन करती है । एक स्ट्रिंग कक्षा को पूरी तरह से परिभाषित करने के बाद बस एक स्ट्रिंग विधि को फिर से परिभाषित करें।

ठीक है, बहुत सारा सामान करता है

एनकैप्सुलेशन एक अच्छा और महत्वपूर्ण सिद्धांत है। लेकिन केवल एक ही नहीं।

switch (principle)
{
      case encapsulation:
           if (there_is_a_reason)
      break!
}

3
"वे मेरे सिद्धांत हैं, और यदि आप उन्हें पसंद नहीं करते हैं ... ठीक है, मेरे पास अन्य हैं।" (ग्रूचो मार्क्स)
रॉन इनबार

2
मुझे लगता है कि यह बिंदु की तरह है। यह इंजेक्शन बनाम एनकैप्सुलेशन पर निर्भर है। तो केवल निर्भर इंजेक्शन का उपयोग करें जहां यह महत्वपूर्ण लाभ देता है। यह हर जगह DI है जो DI को एक बुरा नाम देता है
रिचर्ड टिंगल

निश्चित नहीं है कि यह उत्तर क्या कहना चाह रहा है ... कि DI करते समय इनकैप्सुलेशन का उल्लंघन करना ठीक है, क्योंकि यह "हमेशा ठीक है" क्योंकि यह वैसे भी उल्लंघन होगा, या केवल यह कि DI इनकैप्सुलेशन का उल्लंघन करने का एक कारण हो सकता है? एक और नोट पर, इन दिनों निर्भरता को इंजेक्ट करने के लिए सार्वजनिक निर्माणकर्ताओं या संपत्तियों पर भरोसा करने की अब कोई आवश्यकता नहीं है ; इसके बजाय, हम निजी एनोटेट क्षेत्रों में इंजेक्ट कर सकते हैं , जो सरल (कम कोड) है और इनकैप्सुलेशन को संरक्षित करता है। इसलिए, हम एक ही समय में दोनों सिद्धांतों का लाभ उठा सकते हैं।
रोजेरियो

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

13

हां, डीआई इनकैप्सुलेशन ("सूचना छिपाने" के रूप में भी जाना जाता है) का उल्लंघन करता है।

लेकिन असली समस्या आता है जब डेवलपर्स एक बहाना KISS का उल्लंघन करने के रूप में इसका इस्तेमाल और YAGNI (यह लघु और सरल रखें) (आप गोना नहीं है इसकी जरूरत) सिद्धांतों।

व्यक्तिगत रूप से, मैं सरल और प्रभावी समाधान पसंद करता हूं। जब भी और जहाँ भी उनकी ज़रूरत होती है, मैं ज्यादातर "नई" ऑपरेटर का उपयोग स्टेटफुल डिपेंडेंट पर निर्भर करता है। यह सरल, अच्छी तरह से समझाया हुआ, समझने में आसान और परीक्षण करने में आसान है। तो क्यों नहीं?


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

5

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

अब, मैं मान रहा हूं कि आप उस मामले के खिलाफ तुलना कर रहे हैं जहां बनाई गई वस्तु अपने स्वयं के समझाया वस्तुओं का निर्माण करती है, सबसे अधिक संभावना है कि इसके निर्माता में। डीपी के बारे में मेरी समझ यह है कि हम इस ज़िम्मेदारी को वस्तु से दूर करके किसी और को देना चाहते हैं। उस अंत तक, "कोई और", जो इस मामले में डीपी कंटेनर है, के पास अंतरंग ज्ञान है जो "इनकैप्सुलेशन" का उल्लंघन करता है; लाभ यह है कि यह उस ज्ञान को वस्तु से बाहर खींचता है, अपने आप को। किसी के पास होना है। आपके आवेदन के बाकी नहीं करता है।

मैं इसे इस तरह से सोचूंगा: निर्भरता इंजेक्शन कंटेनर / सिस्टम इनकैप्सुलेशन का उल्लंघन करता है, लेकिन आपका कोड नहीं करता है। वास्तव में, आपका कोड अधिक "एनकैप्सुलेटेड" है तो कभी भी।


3
यदि आपके पास ऐसी स्थिति है जहां ग्राहक ऑब्जेक्ट सीधे अपनी निर्भरता को तुरंत कर सकते हैं, तो ऐसा क्यों नहीं करते? यह निश्चित रूप से सबसे आसान काम है, और जरूरी नहीं कि परीक्षणशीलता कम हो। सरलता और बेहतर एनकैप्सुलेशन के अलावा, यह स्टेटलेस ऑब्जेक्ट्स को स्टेटलेस सिंग्लेटन्स के बजाय आसान बनाना भी आसान बनाता है।
रोगियो जूल 21'09

1
क्या @ Rogério ने कहा कि यह संभावित रूप से काफी अधिक कुशल है। दुनिया के इतिहास में बनाई गई हर वर्ग को अपनी हर एक निर्भरता को मालिक वस्तु के जीवनकाल की संपूर्णता के लिए तुरंत तैयार करने की आवश्यकता नहीं थी। डीआई का उपयोग करने वाली एक वस्तु अपने स्वयं के जीवनकाल के आधार पर अपने स्वयं के जीवन का सबसे बुनियादी नियंत्रण खो देती है।
न्यूट्रिनो

5

के रूप में जेफ स्टर्नल प्रश्न के लिए एक टिप्पणी में बताया, इस सवाल का जवाब पूरी तरह आप कैसे परिभाषित पर निर्भर है कैप्सूलीकरण

लगता है कि एनकैप्सुलेशन के दो मुख्य शिविर हैं:

  1. वस्तु से जुड़ी हर चीज किसी वस्तु पर एक विधि है। तो, एक Fileवस्तु के तरीकों हो सकता है Save, Print, Display, ModifyText, आदि
  2. एक वस्तु अपनी छोटी दुनिया है, और बाहर के व्यवहार पर निर्भर नहीं करती है।

ये दोनों परिभाषाएँ एक-दूसरे के सीधे विरोधाभास में हैं। यदि कोई Fileवस्तु स्वयं प्रिंट कर सकती है, तो यह प्रिंटर के व्यवहार पर बहुत अधिक निर्भर करेगा। दूसरी ओर, अगर यह केवल उस चीज़ के बारे में जानता है जो इसके लिए ( IFilePrinterया कुछ ऐसे इंटरफ़ेस) प्रिंट कर सकती है , तो Fileऑब्जेक्ट को प्रिंटिंग के बारे में कुछ भी पता नहीं है, और इसलिए इसके साथ काम करने से ऑब्जेक्ट में कम निर्भरता आएगी।

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

दूसरी ओर, निर्भरता इंजेक्शन लगभग अनिवार्य है यदि आप एनकैप्सुलेशन की दूसरी परिभाषा का उपयोग कर रहे हैं।


मैं पहली परिभाषा के बारे में आपसे निश्चित रूप से सहमत हूं। यह SoC का भी उल्लंघन करता है जो यकीनन प्रोग्रामिंग के कार्डिनल पापों में से एक है और शायद इसका एक कारण यह नहीं है।
मार्कस स्टेड 14

4

यह इनकैप्सुलेशन का उल्लंघन नहीं करता है। आप एक सहयोगी प्रदान कर रहे हैं, लेकिन वर्ग यह तय करता है कि इसका उपयोग कैसे किया जाता है। जब तक आप कहेंगे बताओ मत पूछो चीजें ठीक हैं। मुझे कंस्ट्रक्टर इंजेक्शन बेहतर लगता है, लेकिन जब तक वे स्मार्ट होते हैं तब तक बसने वाले ठीक हो सकते हैं। वे वे होते हैं जिनमें वर्ग का प्रतिनिधित्व करने वाले आक्रमणकारियों को बनाए रखने के लिए तर्क होते हैं।


1
क्योंकि यह ... नहीं है? यदि आपके पास एक लकड़हारा है, और आपके पास एक वर्ग है जिसे लकड़हारे की आवश्यकता है, तो लकड़हारे को उस वर्ग में पास करना एनकैप्सुलेशन का उल्लंघन नहीं करता है। और यह सब निर्भरता इंजेक्शन है।
jrockway

3
मुझे लगता है कि आप एन्कैप्सुलेशन को गलत समझते हैं। उदाहरण के लिए, एक भोली तारीख वर्ग लें। आंतरिक रूप से इसका दिन, महीना और वर्ष उदाहरण चर हो सकते हैं। यदि ये बिना किसी तर्क के सरल निपटारे के रूप में सामने आते हैं, तो इससे इनकैप्सुलेशन टूट जाएगा, क्योंकि मैं महीने से 2 और दिन को 31 तक सेट कर सकता हूं। दूसरी तरफ, अगर सेटर स्मार्ट हैं और इन्वर्टर की जांच करते हैं, तो चीजें ठीक हैं । यह भी ध्यान दें कि बाद के संस्करण में, मैं 1/1/1970 से दिनों के लिए स्टोरेज को बदल सकता था, और कुछ भी नहीं जो इंटरफ़ेस का उपयोग करता था, इस बारे में पता होना चाहिए, बशर्ते मैं दिन / महीने / वर्ष के तरीकों को फिर से लिखूं।
जेसन वॉटकिंस

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

2
मेरे पास एक ठोस उदाहरण है जहां मुझे लगता है कि डीआईएस द्वारा एनकैप्सुलेशन समझौता किया गया है। मेरे पास एक FooProvider है जो DB से "foo डेटा" प्राप्त करता है और एक FooManager जो इसे कैश करता है और प्रदाता के ऊपर सामान की गणना करता है। मेरे पास मेरे कोड के उपभोक्ता गलती से डेटा के लिए FooProvider में जा रहे थे, जहाँ मुझे लगता था कि यह दूर हो गया है, इसलिए वे केवल FooManager के बारे में जानते हैं। यह मूल रूप से मेरे मूल प्रश्न का ट्रिगर है।
उरई

1
@ रोगेरियो: मेरा तर्क है कि कंस्ट्रक्टर सार्वजनिक इंटरफ़ेस का हिस्सा नहीं है, क्योंकि इसका उपयोग केवल कंपोजीशन रूट में किया जाता है। इस प्रकार, रचना जड़ द्वारा निर्भरता केवल "देखा" है। रचना की जड़ की एकमात्र जिम्मेदारी इन निर्भरताओं को एक साथ तारित करना है। तो कंस्ट्रक्टर इंजेक्शन का उपयोग करने से कोई भी एनकैप्सुलेशन नहीं टूटता है।
जय सुलिवन

4

यह उत्कीर्ण जवाब के समान है लेकिन मैं जोर से सोचना चाहता हूं - शायद दूसरों को भी इस तरह से चीजें दिखाई देती हैं।

  • शास्त्रीय OO वर्ग के उपभोक्ताओं के लिए सार्वजनिक "इनिशियलाइज़ेशन" अनुबंध को परिभाषित करने के लिए कंस्ट्रक्टर्स का उपयोग करता है (सभी कार्यान्वयन विवरणों को छिपाकर उर्फ ​​इनकैप्सुलेशन)। यह अनुबंध सुनिश्चित कर सकता है कि तात्कालिकता के बाद आपके पास एक तैयार-से-उपयोग की जाने वाली वस्तु है (यानी उपयोगकर्ता द्वारा याद किए जाने (एर, भूल गए) के लिए कोई अतिरिक्त आरंभीकरण चरण नहीं)।

  • (कंस्ट्रक्टर) DI इस सार्वजनिक कंस्ट्रक्टर इंटरफेस के माध्यम से अप्रत्यक्ष रूप से रक्तस्राव विस्तार से एनकैप्सुलेशन को तोड़ता है। जब तक हम अभी भी उपयोगकर्ताओं के लिए इनिशियलाइज़ेशन कॉन्ट्रैक्ट को परिभाषित करने के लिए सार्वजनिक निर्माणकर्ता को जिम्मेदार मानते हैं, तब तक हमने एनकैप्सुलेशन का भयानक उल्लंघन किया है।

सैद्धांतिक उदाहरण:

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

यह कहें कि फू के इस विशेष कार्यान्वयन को अपना काम करने के लिए IWidget की आवश्यकता हो सकती है । इस निर्भरता के कंस्ट्रक्टर इंजेक्शन ने हमें फू (इंट आकार, आईवेट विजेट) जैसा एक निर्माता बनाया होगा

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

आकार पैरामीटर एक निर्भरता नहीं है - यह एक प्रति-उदाहरण आरंभीकरण मूल्य सरल है। आईओसी बाहरी निर्भरता (जैसे विजेट) के लिए बांका है, लेकिन आंतरिक स्थिति के प्रारंभ के लिए नहीं।

इससे भी बदतर, क्या होगा अगर इस वर्ग पर विजेट केवल 4 में से 2 तरीकों के लिए आवश्यक है; मैं विजेट के लिए तात्कालिक ओवरहेड हो सकता है, भले ही इसका इस्तेमाल न किया जाए!

इससे समझौता / सामंजस्य कैसे करें?

एक दृष्टिकोण ऑपरेशन अनुबंध को परिभाषित करने के लिए इंटरफेस पर विशेष रूप से स्विच करना है; और उपयोगकर्ताओं द्वारा निर्माणकर्ताओं के उपयोग को समाप्त कर दिया गया। सुसंगत होने के लिए, सभी वस्तुओं को केवल इंटरफेस के माध्यम से एक्सेस किया जाना होगा, और केवल रिवाल्वर (IOC / DI कंटेनर की तरह) के कुछ फॉर्म के माध्यम से त्वरित किया जाएगा। केवल कंटेनर को तुरंत चीजें मिलती हैं।

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

इस डीआई दुनिया में मैं गारंटीकृत आरंभीकरण कैसे प्राप्त कर सकता हूं, जब इनिशियलाइज़ेशन केवल बाहरी निर्भरता से अधिक है?


अपडेट: मैंने अभी देखा कि एकता 2.0 कंस्ट्रक्टर मापदंडों (जैसे राज्य इनिशियलाइज़र) के लिए मान प्रदान करने का समर्थन करती है, जबकि अभी भी रिज़ॉल्यूशन () के दौरान निर्भरता के आईओसी के लिए सामान्य तंत्र का उपयोग कर रही है। शायद अन्य कंटेनर भी इसका समर्थन करते हैं? यह एक निर्माण में राज्य init और DI को मिलाने की तकनीकी कठिनाई को हल करता है, लेकिन यह अभी भी इनकैप्सुलेशन का उल्लंघन करता है!
शाम

मैं आपकी बात सुनता हूं। मैंने यह सवाल इसलिए पूछा क्योंकि मुझे भी लगता है कि दो अच्छी चीजें (DI और एनकैप्सुलेशन) एक दूसरे की कीमत पर आती हैं। BTW, आपके उदाहरण में जहां 4 में से केवल 2 विधियों को IWidget की आवश्यकता होती है, जो यह संकेत देगा कि अन्य 2 एक अलग घटक IMHO में हैं।
यूरिग

3

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

लेकिन अगर आप एक ऐसी कार का निर्माण कर रहे हैं जो एक निश्चित प्रकार की इंजन वस्तु चाहती है तो आपके पास एक बाहरी निर्भरता है। आप या तो उस इंजन को इंस्टेंट कर सकते हैं - उदाहरण के लिए नया GMOverHeadCamEngine () - आंतरिक रूप से कार ऑब्जेक्ट के कंस्ट्रक्टर के भीतर, एनकैप्सुलेशन को संरक्षित करना लेकिन एक ठोस वर्ग GMOverHeadCamineine को और अधिक कपटी कपल बनाना, या आप इसे इंजेक्ट कर सकते हैं, जिससे आपकी कार ऑब्जेक्ट को संचालित करने की अनुमति मिलती है agnostically (और बहुत अधिक मजबूती से) उदाहरण के लिए कंक्रीट निर्भरता के बिना एक इंटरफ़ेस IEngine। चाहे आप इसे प्राप्त करने के लिए एक आईओसी कंटेनर या सरल डि का उपयोग करते हैं, यह बात नहीं है - मुद्दा यह है कि आपको एक कार मिली है जो कई प्रकार के इंजनों को उनमें से किसी के साथ युग्मित किए बिना उपयोग कर सकती है, इस प्रकार आपके कोडबेस को अधिक लचीला बनाता है और दुष्प्रभाव कम होने का खतरा।

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


3

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

यदि आपके पास एक तरीका है जो कॉल को तेज करने के लिए हुड के नीचे कैशिंग का उपयोग करता है, तो कैश ऑब्जेक्ट एक सिंगलटन या कुछ और होना चाहिए और इंजेक्शन नहीं होना चाहिए । तथ्य यह है कि कैश का उपयोग किया जा रहा है एक कार्यान्वयन विवरण है कि आपके वर्ग के ग्राहकों को परवाह नहीं करनी चाहिए।

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

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


2

मैं सादगी में विश्वास करता हूं। डोमेन की कक्षाओं में IOC / Dependecy इंजेक्शन लगाने से कोड को और अधिक कठिन बनाने के अलावा कोई सुधार नहीं होता है, बाहरी एक्सएमएल फ़ाइलों का संबंध बताकर। EJB 1.0 / 2.0 और struts 1.1 जैसी कई प्रौद्योगिकियां XML में पुट को कम करके वापस ले जा रही हैं और उन्हें एनोटेशन आदि के रूप में कोड में डालने की कोशिश कर रही हैं। इसलिए आपके द्वारा विकसित सभी वर्गों के लिए IOC लागू करने से कोड गैर-समझ में आ जाएगा।

आईओसी के पास यह लाभ है जब आश्रित वस्तु संकलन के समय निर्माण के लिए तैयार नहीं है। यह अधिकांश इन्फ्रास्ट्रक्चर अमूर्त स्तर वास्तुकला घटकों में खुश कर सकता है, एक सामान्य आधार ढांचे की स्थापना करने की कोशिश कर रहा है जिसे विभिन्न परिदृश्यों के लिए काम करने की आवश्यकता हो सकती है। उन स्थानों में IOC का उपयोग अधिक मायने रखता है। फिर भी यह कोड को अधिक सरल / बनाए रखने योग्य नहीं बनाता है।

अन्य सभी तकनीकों की तरह, इसमें भी PRO & CONs हैं। मेरी चिंता यह है कि हम सभी क्षेत्रों में नवीनतम तकनीकों को लागू करते हैं, भले ही उनके सर्वोत्तम संदर्भ उपयोग के बावजूद।


2

एनकैप्सुलेशन केवल तभी टूट जाता है जब किसी वर्ग के पास ऑब्जेक्ट बनाने के लिए ज़िम्मेदारी होती है (जिसे कार्यान्वयन विवरणों के ज्ञान की आवश्यकता होती है) और फिर क्लास का उपयोग करता है (जिसे इन विवरणों के ज्ञान की आवश्यकता नहीं होती है)। मैं समझाता हूँ क्यों, लेकिन पहले एक त्वरित कार एनोलॉजी:

जब मैं अपनी पुरानी 1971 कोम्बी चला रहा था, मैं त्वरक दबा सकता था और यह (थोड़ा) तेज हो गया था। मुझे यह जानने की आवश्यकता नहीं थी कि कारखाने में कोम्बी का निर्माण करने वाले लोग वास्तव में क्यों जानते थे।

लेकिन वापस कोडिंग के लिए। encapsulation "उस कार्यान्वयन का उपयोग करके कुछ से कार्यान्वयन विवरण छिपा रहा है।" एनकैप्सुलेशन एक अच्छी बात है क्योंकि कार्यान्वयन विवरण को जानने वाले वर्ग के उपयोगकर्ता के बिना बदल सकता है।

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

निर्माता जब कक्षा शुरू में बनाया जा रहा है प्रासंगिक है। यह निर्माण-चरण के दौरान होता है जबकि आपके DI कंटेनर (या कारखाना) सभी सेवा वस्तुओं को एक साथ तार करते हैं। DI कंटेनर केवल कार्यान्वयन विवरण के बारे में जानता है। यह सभी कार्यान्वयन विवरणों के बारे में जानता है जैसे कोम्बी कारखाने के लोग स्पार्क प्लग के बारे में जानते हैं।

रन-टाइम पर, जो सेवा ऑब्जेक्ट बनाया गया था, उसे कुछ वास्तविक कार्य करने के लिए एपन कहा जाता है। इस समय, ऑब्जेक्ट का कॉलर कार्यान्वयन विवरणों में से कुछ भी नहीं जानता है।

यही कारण है कि मैं अपनी कोम्बी को समुद्र तट पर चला रहा हूं।

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

मैं अपनी नई कार को समुद्र तट पर भी चला सकता हूं। एनकैप्सुलेशन टूटा नहीं है।

यदि कार्यान्वयन विवरण बदलते हैं, तो डीआई कंटेनर (या कारखाना) को बदलने की आवश्यकता है। आप पहली बार कारखाने से कार्यान्वयन विवरण को छिपाने की कोशिश नहीं कर रहे थे।


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

2

इस मुद्दे से थोड़ा आगे बढ़ने के बाद, मैं अब इस राय पर कायम हूं कि डिपेंडेंसी इंजेक्शन (इस समय) कुछ हद तक एनकैप्सुलेशन का उल्लंघन करता है। हालांकि मुझे गलत मत समझो - मुझे लगता है कि निर्भरता इंजेक्शन का उपयोग करना ज्यादातर मामलों में व्यापार के लायक है।

DI ने एनकैप्सुलेशन का उल्लंघन क्यों किया, इसके लिए मामला तब स्पष्ट हो जाता है जब आप जिस घटक पर काम कर रहे हैं उसे "बाहरी" पार्टी (एक ग्राहक के लिए एक पुस्तकालय लिखने के बारे में सोचें) को वितरित किया जाना है।

जब मेरे घटक को कंस्ट्रक्टर (या सार्वजनिक संपत्तियों) के माध्यम से इंजेक्ट करने के लिए उप-घटकों की आवश्यकता होती है, तो इसके लिए कोई गारंटी नहीं है

"घटक के आंतरिक डेटा को अमान्य या असंगत स्थिति में स्थापित करने से उपयोगकर्ताओं को रोकना"।

उसी समय ऐसा नहीं कहा जा सकता है

"घटक के उपयोगकर्ता (सॉफ़्टवेयर के अन्य टुकड़े) को केवल यह जानने की जरूरत है कि घटक क्या करता है, और यह कैसे करता है के विवरण पर खुद को निर्भर नहीं कर सकता है"

दोनों उद्धरण विकिपीडिया के हैं

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

आम तौर पर मैं सभी DI के लिए हूं। इस विशेष (चरम) उदाहरण में, यह मुझे खतरनाक के रूप में प्रभावित करता है।


2

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

DI के अधिवक्ताओं का तर्क है कि किसी ऑब्जेक्ट को नए ऑपरेटर का उपयोग नहीं करना चाहिए। वह "शुद्धतावादी" दृष्टिकोण न केवल एनकैप्सुलेशन का उल्लंघन करता है, बल्कि जो भी वस्तु का निर्माण कर रहा है उसके लिए लिस्कोव सबस्टीट्यूशन सिद्धांत है।


1

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

वास्तव में यह और भी बेहतर हो जाता है - कारखानों और प्रस्तावों पर ध्यान केंद्रित करके मुझे यह भी पता नहीं है कि DI बिल्कुल भी शामिल नहीं है! कि मुझे ढक्कन वापस encapsulation पर डालता है। वाह!


1
जब तक डीआई तात्कालिकता की पूरी श्रृंखला के प्रभारी हैं, तब तक मैं मानता हूं कि एनकैप्सुलेशन प्रकार होता है। क्रमबद्ध करें, क्योंकि निर्भरताएं अभी भी सार्वजनिक हैं और दुरुपयोग किया जा सकता है। लेकिन जब चेन में कहीं "अप" होता है, तो किसी को डीआई (शायद वे "थर्ड पार्टी" हैं) का उपयोग किए बिना किसी वस्तु को तुरंत हटाने की आवश्यकता होती है, तो यह गड़बड़ हो जाता है। वे आपकी निर्भरता के संपर्क में हैं और उनका दुरुपयोग करने का प्रलोभन दिया जा सकता है। या वे उनके बारे में बिल्कुल भी जानना नहीं चाहेंगे।
उरिग 28:09

1

पुनश्च। निर्भरता इंजेक्शन प्रदान करके आप जरूरी नहीं कि एनकैप्सुलेशन को तोड़ दें । उदाहरण:

obj.inject_dependency(  factory.get_instance_of_unknown_class(x)  );

ग्राहक कोड कार्यान्वयन विवरण अभी भी नहीं जानता है।


आपके उदाहरण में कुछ भी कैसे इंजेक्ट किया गया है? (अपने सेटर समारोह के नामकरण को छोड़ कर)
foo

1

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

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


यह मूल्य बनाम सेवा के बारे में नहीं है। यह नियंत्रण के उलट के बारे में है - क्या वर्ग का निर्माणकर्ता वर्ग को सेट करता है, या क्या यह सेवा कक्षा को स्थापित करने में लगती है। जिसके लिए उसे उस वर्ग के कार्यान्वयन विवरण को जानना होगा, इसलिए आपको बनाए रखने और सिंक करने के लिए एक और जगह मिल गई।
फू

1

मुझे लगता है कि यह स्पष्ट है कि बहुत कम से कम डि काफी कमज़ोर पड़ जाता है। इसके अतिरिक्त यहाँ डीआई के कुछ अन्य विचार हैं।

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

  2. यह कोड को अधिक नाजुक बना सकता है। इंटरफ़ेस विनिर्देश द्वारा प्रदान की जाने वाली निर्भरता अप्रत्याशित तरीकों से लागू की जा सकती है जो रनटाइम बग की एक पूरी श्रेणी को जन्म देती है जो एक सांख्यिकीय रूप से हल किए गए कंक्रीट निर्भरता के साथ संभव नहीं है।

  3. यह इस अर्थ में कोड को कम लचीला बना सकता है कि आप कम विकल्पों के साथ समाप्त कर सकते हैं कि आप इसे कैसे काम करना चाहते हैं। प्रत्येक वर्ग को अपने पूरे जीवनकाल के लिए स्वयं के उदाहरण के लिए अस्तित्व में होने की आवश्यकता नहीं है, फिर भी कई DI कार्यान्वयन के साथ आपके पास कोई विकल्प नहीं है।

इस बात को ध्यान में रखते हुए कि मुझे सबसे महत्वपूर्ण प्रश्न लगता है, " क्या किसी विशेष निर्भरता को बाहरी रूप से निर्दिष्ट करने की आवश्यकता है? "। व्यवहार में, मैंने शायद ही कभी यह पाया है कि केवल परीक्षण का समर्थन करने के लिए एक निर्भरता को बाहरी रूप से आपूर्ति करना आवश्यक है।

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

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

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

तो सवाल का मेरा जवाब यह है। DI का उपयोग करें जहां आप एक वास्तविक समस्या की पहचान कर सकते हैं जो आपके लिए हल करती है, जिसे आप किसी अन्य तरीके से अधिक हल नहीं कर सकते हैं।


0

मैं मानता हूं कि अति पर ले जाया गया, DI इनकैप्सुलेशन का उल्लंघन कर सकता है। आमतौर पर DI निर्भरता को उजागर करता है जो कभी सही मायने में समझाया नहीं गया था। यहाँ मिश्को हेवरी के सिंगलेट्स से उधार लिया गया एक सरल उदाहरण पैथोलॉजिकल लियर्स हैं :

आप एक CreditCard परीक्षण से शुरू करते हैं और एक साधारण इकाई परीक्षण लिखते हैं।

@Test
public void creditCard_Charge()
{
    CreditCard c = new CreditCard("1234 5678 9012 3456", 5, 2008);
    c.charge(100);
}

अगले महीने आपको $ 100 का बिल मिलेगा। आपसे शुल्क क्यों लिया गया? इकाई परीक्षण ने एक उत्पादन डेटाबेस को प्रभावित किया। आंतरिक रूप से, क्रेडिटकार्ड कॉल करता है Database.getInstance()। क्रडिटकार्ड को फिर से तैयार करना ताकि DatabaseInterfaceइसके निर्माण में लगे इस तथ्य को उजागर किया जाए कि निर्भरता है। लेकिन मैं तर्क दूंगा कि क्रेडिट कार्ड के बाहरी स्तर पर दिखाई देने वाले दुष्प्रभावों के कारण से निर्भरता कभी शुरू नहीं हुई थी। यदि आप बिना रिफैक्टिंग के क्रेडिटकार्ड का परीक्षण करना चाहते हैं, तो आप निश्चित रूप से निर्भरता का निरीक्षण कर सकते हैं।

@Before
public void setUp()
{
    Database.setInstance(new MockDatabase());
}

@After
public void tearDown()
{
    Database.resetInstance();
}

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


1
यूनिट परीक्षण आमतौर पर कक्षा के लेखक द्वारा लिखे जाते हैं, इसलिए तकनीकी मामले से परीक्षण के मामले में निर्भरता को बाहर करना ठीक है। जब पेपल जैसे वेब एपीआई का उपयोग करने के लिए बाद में क्रेडिट कार्ड क्लास बदल जाता है, तो उपयोगकर्ता को सब कुछ बदलने की आवश्यकता होती है यदि यह डीआईडेड था। यूनिट परीक्षण आमतौर पर विषय के अंतरंग ज्ञान के साथ किया जाता है (जो कि संपूर्ण बिंदु नहीं है?) इसलिए मुझे लगता है कि परीक्षण एक विशिष्ट उदाहरण से अधिक अपवाद हैं।
kizzx2

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

@ क्रैग पी। Motlin CreditCardएक WebAPI से PayPal में परिवर्तन कैसे हो सकता है , बिना किसी बाहरी चीज के वर्ग में परिवर्तन हो सकता है?
इयान बॉयड

@ यदि मैंने उल्लेख किया है कि CreditCard को इसके कंस्ट्रक्टर में एक डेटाबेसइंटरफेस लेने के लिए फिर से तैयार किया जाना चाहिए, जो इसे डेटाबेसइंटरफेसेस के कार्यान्वयन में बदलाव से बचाता है। हो सकता है कि इसके लिए और भी अधिक सामान्य होने की आवश्यकता है, और एक StorageInterface लेना चाहिए जिसमें WebAPI एक और कार्यान्वयन हो सकता है। हालांकि, पेपाल गलत स्तर पर है, क्योंकि यह क्रेडिटकार्ड का विकल्प है। PayPal और CreditCard दोनों इस उदाहरण के बाहर से एप्लिकेशन के अन्य हिस्सों को ढालने के लिए PaymentInterface को लागू कर सकते हैं।
क्रेग पी। मोटलिन

@ kizzx2 दूसरे शब्दों में, यह कहना बकवास है कि क्रेडिट कार्ड को पेपाल का उपयोग करना चाहिए। वे वास्तविक जीवन में विकल्प हैं।
क्रेग पी। मोटलिन

0

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

  1. क्लास इस प्रकार है : आप जो एनकैप्सुलेट कर रहे हैं वह क्लास की एकमात्र जिम्मेदारी है। यह क्या करना है जानता है। उदाहरण के अनुसार, छँटाई। यदि आप ऑर्डर करने के लिए कुछ तुलनित्र को इंजेक्ट करते हैं, तो हम कहते हैं, क्लाइंट्स, जो इनकैप्सुलेटेड चीज़ का हिस्सा नहीं है: क्विकॉर्ट।

  2. कॉन्फ़िगर की गई कार्यक्षमता : यदि आप एक रेडी-टू-यूज़ कार्यक्षमता प्रदान करना चाहते हैं तो आप क्विकसॉर्ट क्लास प्रदान नहीं कर रहे हैं, लेकिन क्विकॉर्ट क्लास का एक उदाहरण एक तुलनित्र के साथ कॉन्फ़िगर किया गया है। उस स्थिति में कोड को बनाने और कॉन्फ़िगर करने के लिए जिम्मेदार है जिसे उपयोगकर्ता कोड से छिपाया जाना चाहिए। और वह इनकैप्सुलेशन है।

जब आप क्लास प्रोग्रामिंग कर रहे होते हैं, तो यह एकल जिम्मेदारियों को कक्षाओं में लागू करता है, आप विकल्प 1 का उपयोग कर रहे हैं।

जब आप अनुप्रयोगों को प्रोग्रामिंग कर रहे होते हैं, तो यह कुछ ऐसा बनाता है जो कुछ उपयोगी ठोस कार्य करता है फिर आप विकल्प 2 का उपयोग करते हुए दोहराते हैं।

यह कॉन्फ़िगर किए गए उदाहरण का कार्यान्वयन है:

<bean id="clientSorter" class="QuickSort">
   <property name="comparator">
      <bean class="ClientComparator"/>
   </property>
</bean>

कुछ अन्य क्लाइंट कोड इसका उपयोग कैसे करते हैं:

<bean id="clientService" class"...">
   <property name="sorter" ref="clientSorter"/>
</bean>

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


0

यह शायद ध्यान देने योग्य है कि Encapsulationकुछ हद तक निर्भर है।

public class A { 
    private B b;

    public A() {
        this.b = new B();
    }
}


public class A { 
    private B b;

    public A(B b) {
        this.b = b;
    }
}

Aकक्षा में काम करने वाले किसी व्यक्ति के दृष्टिकोण से , दूसरे उदाहरण Aमें प्रकृति के बारे में बहुत कम पता हैthis.b

जबकि बिना डी.आई.

new A()

बनाम

new A(new B())

इस कोड को देखने वाला व्यक्ति Aदूसरे उदाहरण में प्रकृति के बारे में अधिक जानता है ।

डीआई के साथ, कम से कम सभी लीक किए गए ज्ञान एक ही स्थान पर हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.