निर्भरता अन्तःक्षेपण ; बॉयलरप्लेट कोड को कम करने के लिए अच्छा अभ्यास


25

मेरे पास एक आसान सवाल है, और मुझे यकीन नहीं है कि इसका जवाब है लेकिन चलो कोशिश करते हैं। मैं C ++ में कोडिंग कर रहा हूं, और वैश्विक स्थिति से बचने के लिए निर्भरता इंजेक्शन का उपयोग कर रहा हूं। यह काफी अच्छी तरह से काम करता है, और मैं बहुत बार अप्रत्याशित / अपरिभाषित व्यवहार में नहीं चलता हूं।

हालाँकि मुझे एहसास है कि, जैसा कि मेरी परियोजना बढ़ती है, मैं बहुत सारे कोड लिख रहा हूं, जिन्हें मैं बॉयलरप्लेट मानता हूं। इससे भी बदतर: तथ्य यह है कि अधिक बॉयलरप्लेट कोड है, वास्तविक कोड की तुलना में इसे कभी-कभी समझना मुश्किल होता है।

कुछ भी नहीं एक अच्छा उदाहरण धड़कता है तो चलें:

मेरे पास टाइमफैक्टरी नामक एक क्लास है जो टाइम ऑब्जेक्ट बनाता है।

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

मेरे ऐप में टाइम ऑब्जेक्ट बनाने के लिए बहुत सारी कक्षाओं की आवश्यकता है। कभी-कभी उन वर्गों का गहरा संबंध होता है।

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

अपने भोले-भाले कार्यान्वयन में, मैं क्लास ए के कंस्ट्रक्टर को टाइमफैक्टरी पास करता हूं, जो क्लास बी के कंस्ट्रक्टर को पास करता है और क्लास डी तक।

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

मुझे आश्चर्य हो रहा है कि क्या मेरे ऐप में एक प्रमुख डिज़ाइन दोष नहीं है ... या क्या यह निर्भरता इंजेक्शन का उपयोग करने की एक आवश्यक बुराई है?

तुम क्या सोचते हो ?


5
मैंने स्टैकओवरफ़्लो के बजाय प्रोग्रामर को यह प्रश्न पोस्ट किया क्योंकि, जैसा कि मैंने समझा था, प्रोग्रामर डिज़ाइन से संबंधित खोज के लिए अधिक है और स्टैकओवरफ़्लो "जब आप अपने कंपाइलर के सामने होते हैं" -सुरक्षा के लिए है। गलत होने पर अग्रिम क्षमा करें।
दिनाज

2
पहलू उन्मुख प्रोग्रामिंग, इस कार्य के लिए भी अच्छा है - en.wikipedia.org/wiki/Aspect-oriented_programming
EL Yusubov

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

D को समय ऑब्जेक्ट बनाने की आवश्यकता है, केवल D के पास जानकारी होनी चाहिए। मुझे आपकी बाकी टिप्पणी के बारे में सोचना है। मेरे कोड में "कौन बना रहा है" में समस्या हो सकती है
दिनाज़

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

जवाबों:


23

मेरे ऐप में टाइम ऑब्जेक्ट बनाने के लिए बहुत सारी कक्षाओं की आवश्यकता है

लगता है कि आपकी Timeकक्षा एक बहुत ही बुनियादी डेटा प्रकार है जो आपके आवेदन के "सामान्य बुनियादी ढांचे" से संबंधित होना चाहिए। डीआई ऐसे वर्गों के लिए अच्छा काम नहीं करता है। इस बारे में सोचें कि इसका क्या मतलब है अगर किसी वर्ग stringको कोड के प्रत्येक भाग में इंजेक्ट किया जाना चाहिए जो स्ट्रिंग्स का उपयोग करता है, और आपको stringFactoryनए स्ट्रिंग्स बनाने के एकमात्र एकमात्र उपयोग के रूप में उपयोग करने की आवश्यकता होगी - आपके प्रोग्राम की पठनीयता एक क्रम से घट जाएगी परिमाण।

तो मेरा सुझाव: सामान्य डेटाटाइप्स के लिए DI का उपयोग न करें TimeTimeकक्षा के लिए इकाई परीक्षण स्वयं लिखें , और जब यह किया जाता है, तो इसे अपने कार्यक्रम में हर जगह उपयोग करें, जैसे stringकक्षा, या vectorकक्षा या मानक परिवाद के किसी अन्य वर्ग में। डीआई का उपयोग उन घटकों के लिए करें जिन्हें वास्तव में एक दूसरे से अलग करना चाहिए।


आपने इसे पूरी तरह सही पाया। "परिमाण के एक आदेश से आपके कार्यक्रम की पठनीयता घट जाएगी।" : बिलकुल ऐसा ही हुआ हाँ। बशर्ते मैं अभी भी एक फैक्ट्री का उपयोग करना चाहता हूं, तो यह एक सिंगलटन बनाने के लिए बुरा नहीं है?
दिनाज

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

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

1
@Dinaiz: ईमानदारी से, यह निर्भर करता है - आपके प्रोग्राम का उपयोग करने वाले Timeऔर TimeFactory, "evolvability" की डिग्री पर, जिस तरह के राज्य के प्रकार पर, "evolvability" से संबंधित भविष्य के एक्सटेंशन के लिए आपको ज़रूरत है TimeFactory, और इसी तरह।
डॉक ब्राउन

ठीक है, मैं डिपेंडेंसी इंजेक्शन रखने की कोशिश करूंगा, लेकिन एक अधिक चतुर डिजाइन है जिसके लिए इतनी बॉयलरप्लेट की आवश्यकता नहीं है! बहुत बहुत धन्यवाद '
दिनाज

4

"मैं सभी लचीलेपन और पठनीयता को ढीला करता हूँ, जो मुझे निर्भरता इंजेक्शन का उपयोग करने के लिए लगता है" का अर्थ है - DI पठनीयता के बारे में नहीं है। यह वस्तुओं के बीच निर्भरता को कम करने के बारे में है।

ऐसा लगता है कि आपके पास क्लास ए, क्लास बी बनाने वाला क्लास बी, क्लास सी बनाने और क्लास सी बनाने वाला क्लास डी है।

आपके पास क्या होना चाहिए, क्लास ए को क्लास ए में इंजेक्ट किया जाएगा। क्लास बी को क्लास बी में इंजेक्ट किया जाएगा। क्लास डी को क्लास सी में इंजेक्ट किया जाएगा।


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

DI ज्यादातर पठनीयता बढ़ाते हैं क्योंकि "नया / हटाएं" आपके कोड से जादुई रूप से छीन लिया जाएगा। यदि आपको डायनामिक आवंटन कोड के बारे में चेतावनी नहीं है तो पढ़ना आसान है।
GameDeveloper

यह भी कहना भूल जाते हैं कि यह कोड को और अधिक मजबूत बनाता है क्योंकि अब "कैसे" एक निर्भरता बनाई जाती है, इसके बारे में धारणा नहीं बना सकते हैं, इसे "बस जरूरत है"। और इससे कक्षा को बनाए रखना आसान हो जाता है .. मेरा जवाब देखें
GameDeveloper

3

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

यह कहा जा रहा है, एक उत्परिवर्तित वस्तु को साझा करना बहुत खतरनाक है, सिवाय इसके कि यह ब्लॉक को सिंक्रोनाइज़ करके सही तरीके से संरक्षित है, इस स्थिति में, इसके सिंगनल न होने का कोई कारण नहीं है।

यदि आप निर्भरता इंजेक्शन करना चाहते हैं, तो आप वसंत या अन्य निर्भरता इंजेक्शन ढांचे को देखना चाहते हैं, जो आपको एनोटेशन का उपयोग करके ऑटो असाइन करने के लिए पैरामीटर की अनुमति देगा।


वसंत जावा के लिए है और मैं सी ++ का उपयोग करता हूं। लेकिन 2 लोगों ने आपको नीचा क्यों दिखाया? आपके पोस्ट में ऐसे बिंदु हैं
जिनसे

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

1

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

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

एक सिंगलटन? जी नहीं, धन्यवाद। यदि आपको केवल एक इस् टेंस की आवश्यकता है, तो इसे अपने आवेदन के संदर्भ में साझा करें (डिओ इंफ़ोरर ++ के लिए एक आईओसी कंटेनर का उपयोग करके, केवल टाइमफ़ेक्ट को एकल इस् टेंस के रूप में बाँधने की आवश्यकता है), यहाँ उदाहरण है (C ++ 11 वैसे, लेकिन C ++। C ++ 11 को पहले से ही (आपको मुफ्त में लीक से मुक्त आवेदन मिलता है):

Infector::Container ioc; //your app's context

ioc.bindSingleAsNothing<TimeFactory>(); //declare TimeFactory to be shared
ioc.wire<TimeFactory>(); //wire its constructor 

// if you want to be sure TimeFactory is created at startup just request it
// (else it will be created lazily only when needed)
auto myTimeFactory = ioc.buildSingle<TimeFactory>();

अब आईओसी कंटेनर का अच्छा बिंदु यह है कि आपको डी तक टाइम फैक्ट्री पास करने की जरूरत नहीं है अगर आपकी क्लास को "डी" टाइम फैक्ट्री की जरूरत है, तो क्लास डी के लिए कंस्ट्रक्टर पैरामीटर के रूप में टाइम फैक्ट्री लगाएं।

ioc.bindAsNothing<A>(); //declare class A
ioc.bindAsNothing<B>(); //declare class B
ioc.bindAsNothing<D>(); //declare class D

//constructors setup
ioc.wire<D, TimeFactory>(); //time factory injected to class D
ioc.wire<B, D>(); //class D injected to class B
ioc.wire<A, B>(); //class B injected to class A

जैसा कि आप देखते हैं कि आप केवल एक बार TimeFactory इंजेक्षन करते हैं। "ए" का उपयोग कैसे करें? बहुत ही सरल, हर वर्ग को इंजेक्ट किया जाता है, मुख्य कारखाने में बनाया जाता है या कारखाने से अलग किया जाता है।

auto myA1 = ioc.build<A>(); //A is not "single" so many different istances
auto myA2 = ioc.build<A>(); //can live at same time

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

D, केवल D के पास हो सकने वाली सूचना के साथ समय ऑब्जेक्ट्स बना सकता है

यह आसान है, आपके TimeFactory में एक "create" विधि है, फिर बस एक अलग हस्ताक्षर "create (params)" का उपयोग करें और आप कर रहे हैं। पैरामीटर जो कोई निर्भरता नहीं हैं, अक्सर इस तरह से हल किए जाते हैं। यह "स्ट्रिंग्स" या "पूर्णांक" जैसी चीजों को इंजेक्ट करने के कर्तव्य को भी हटा देता है, क्योंकि यह केवल अतिरिक्त बॉयलर प्लेट को जोड़ता है।

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

//factory method
std::unique_ptr<myType> create(params){
    auto istance = ioc->build<myType>(); //this code's agnostic to "myType" hierarchy
    istance->setParams(params); //the customization you needed
    return std::move(istance); 
}

निर्भरता इंजेक्शन का दुरुपयोग न करें, सरल प्रकार केवल कक्षा के सदस्य या स्थानीय स्कोप किए गए चर हो सकते हैं। यह स्पष्ट प्रतीत होता है, लेकिन मैंने देखा कि लोग "std :: वेक्टर" को सिर्फ इसलिए इंजेक्ट कर रहे थे क्योंकि एक DI फ्रेमवर्क था जो इसकी अनुमति देता था। हमेशा Demeter के नियम को याद रखें: "केवल वही इंजेक्ट करें जो आपको वास्तव में इंजेक्ट करने की आवश्यकता है"


वाह, मैंने आपका जवाब पहले नहीं देखा, क्षमा करें! बहुत जानकारीपूर्ण सर! अपविटेड
दीनीज़

0

क्या आपके ए, बी और सी वर्गों को भी समय उदाहरण, या केवल कक्षा डी बनाने की आवश्यकता है? यदि यह केवल कक्षा डी है, तो ए और बी को टाइमफैक्ट्री के बारे में कुछ भी नहीं पता होना चाहिए। C वर्ग के अंदर TimeFactory का एक उदाहरण बनाएँ, और इसे कक्षा D. में पास करें। ध्यान दें कि "एक उदाहरण बनाएँ" से मेरा मतलब यह नहीं है कि TimeFactory को तुरंत करने के लिए C वर्ग को जिम्मेदार होना होगा। यह कक्षा B से DClassFactory प्राप्त कर सकता है, और DClassFactory जानता है कि समय उदाहरण कैसे बनाया जाए।

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


-1

मैंने अभी तक एक और C ++ निर्भरता इंजेक्शन फ्रेमवर्क लागू किया है, जिसे हाल ही में बढ़ावा देने के लिए प्रस्तावित किया गया था - https://github.com/krzysztof-jusiak/di - पुस्तकालय मैक्रो कम (मुक्त) है, केवल हेडर, C ++ 03 / C ++ 11 / C ++ 14 पुस्तकालय प्रकार सुरक्षित, संकलन समय, मैक्रो मुक्त कंस्ट्रक्टर निर्भरता इंजेक्शन प्रदान करते हैं।


3
P.SE पर पुराने प्रश्नों का उत्तर देना आपके प्रोजेक्ट का विज्ञापन करने का एक खराब तरीका है। विचार करें कि वहाँ दसियों हजारों परियोजनाएँ हैं जिनमें कुछ प्रश्न के लिए कुछ प्रासंगिकता हो सकती है। यदि उनके सभी लेखक यहां पोस्ट करते हैं, तो साइट की उत्तर गुणवत्ता घटती जाएगी।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.