2012-03-13 20 views
9

मेरे पास कक्षा ए है जो 10 अन्य वर्गों पर निर्भर करता है। निर्भरता इंजेक्शन पैटर्न के अनुसार, मुझे अपने कन्स्ट्रक्टर द्वारा ए की सभी निर्भरताओं को पारित करना चाहिए।निर्भरता इंजेक्शन - जब आपके पास बहुत निर्भरताएं होती हैं तो क्या करें?

तो यह निर्माता मान (निश्चित रूप से यह एक काम या वास्तविक कोड नहीं है, के बाद से मैं यहाँ वास्तविक कोड पोस्ट करने की अनुमति नहीं कर रहा हूँ)

public ClassA(ClassB b, ClassC c, ClassD d, ClassE e, ClassF f, ClassG g, ClassH h, ClassI i) { 
    this.b = b; 
    this.c = c; 
    this.d = d; 
    this.e = e; 
    this.f = f; 
    this.g = g; 
    this.h = h; 
    this.i = i; 
} 

मैं पुनर्रचना के बारे में मार्टिन Fowler की किताब पर पढ़ा है देता है कि बहुत सारे पैरामीटर के साथ एक विधि है एक कोड गंध है और नहीं होना चाहिए।

मेरा प्रश्न है: क्या यह ठीक है जब हम DI के बारे में बात कर रहे हैं? क्या मार्टिन फाउलर के नियमों को तोड़ने के बिना निर्भरताओं को इंजेक्ट करने का कोई बेहतर तरीका है?

मुझे पता है कि मैं गुणों के माध्यम से निर्भरता पारित कर सकता हूं, लेकिन इससे त्रुटियां हो सकती हैं क्योंकि कोई भी वास्तव में यह सुनिश्चित नहीं करता है कि वर्ग काम करता है या नहीं।

संपादित

अपने सभी सवालों के जवाब के लिए धन्यवाद। मैं अब कक्षा एक निर्भरता से कुछ का प्रदर्शन करने की कोशिश करेंगे: - एक डीबी
2 उपयोग करने के लिए एक वर्ग -

1 एक और डीबी तक पहुँचने के लिए एक अन्य वर्ग (हाँ, मैं दो डेटाबेस पर कार्रवाई करने की जरूरत है)
3 - एक वर्ग ईमेल
4 से त्रुटि सूचनाएं भेजने के लिए - एक वर्ग विन्यास लोड करने के लिए
5 - एक वर्ग है कि
6 कुछ कार्यों (शायद यह एक बचा जा सकता है) के लिए टाइमर के रूप में कार्य करेगा - व्यापार तर्क के साथ एक वर्ग

कोई भी अन्य जो मैं छुटकारा पाने की कोशिश कर रहा हूं, लेकिन वे आर हैं ईली आवश्यक है और मैं उन्हें टालने के किसी भी तरीके से नहीं देखता हूं।

संपादित

कुछ पुनर्रचना के बाद अब मैं 7 निर्भरता (10 से नीचे) है।

CustomerDAO
ProcessDAO
ProductsDAO
CatalogDAO

यह सही अन्य वर्ग MyProjectDAO बुलाया बनाने और इसे पर उन DAOS इंजेक्षन करते है: लेकिन मैं 4 DAO वस्तुओं है? इस तरह मेरे पास केवल एक डीएओ कक्षा होगी जो मेरी परियोजना के सभी डीएओ वस्तुओं को एकत्रित करेगी। मुझे नहीं लगता कि यह एक अच्छा विचार है क्योंकि यह एकल जिम्मेदारी सिद्धांत का उल्लंघन करता है। क्या मैं सही हू?

+0

संभावित निर्भरता [निर्भरता इंजेक्शन कन्स्ट्रक्टर पागलपन] (http://stackoverflow.com/questions/2420193/dependency-injection-constructor-madness) –

+0

जब आप किसी प्रकार के ओआरएम का उपयोग करते हैं तो सभी 4 डीएओ को प्रतिस्थापित किया जा सकता है UnitOfWork ऑब्जेक्ट। – Firo

उत्तर

7

क्या आप औचित्य साबित कर सकते हैं कि कक्षा 10 अन्य कक्षाओं पर क्यों निर्भर करती है? क्या वे सदस्य चर हैं जिनका उपयोग आप उन वर्गों के उप-समूह को जोड़ने के लिए करते हैं? यदि ऐसा है, तो यह इंगित करता है कि इस वर्ग को तोड़ दिया जाना चाहिए ताकि निकाली गई कक्षा सबसेट पर निर्भर करे और ऐसे राज्य जो इस तरह के राज्य को एक साथ जोड़ते हैं, निकाले गए वर्ग में जाते हैं। 10 निर्भरताओं के साथ, यह संभव है कि यह वर्ग बस इतना बड़ा हो गया है और इसके आंतरिक भी किसी भी तरह टूटने की जरूरत है।

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

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

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

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

2 संपादित करें (आपके संपादन के आधार):

मेरी पहली सोचा कि "क्या, कोई प्रवेश निर्भरता?" :)

यह भी जानना कि निर्भरता क्या है, उपयोगी सलाह देना मुश्किल है।

पहला: सभी की जिम्मेदारियां क्या हैं? यह वर्ग नियंत्रक कोड (व्यापार तर्क) और मॉडल कोड (डीएओ कक्षाओं के साथ दो अलग-अलग डेटाबेस एक्सेस क्लास) पर निर्भर क्यों है?

डीएओ और डीबी एक्सेस कक्षाओं दोनों के आधार पर एक कोड गंध है। डीएओ का उद्देश्य क्या है? डीबी कक्षाओं का उद्देश्य क्या है? क्या आप अमूर्तता के कई स्तरों पर काम करने की कोशिश कर रहे हैं?

ओओ के सिद्धांतों में से एक यह है कि डेटा और व्यवहार कक्षाओं नामक छोटी चीजों में बंडल हो जाता है। क्या आपने इसका उल्लंघन किया है जब आपने इस व्यवसाय तर्क वर्ग को उन वस्तुओं से अलग किया है जो इस कक्षा से अलग डीएओ से अलग हैं? संबंधित: SOLID में एक संक्षिप्त मोड़ लें।

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

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

+0

कृपया मेरा संपादन देखें। –

+0

@RafaelColucci - – ccoakley

+0

अपडेट किया गया है, भले ही आपको दूसरों के रूप में कई अपवॉट नहीं मिला है, फिर भी मैं आपको जवाब स्वीकार करना चुनता हूं क्योंकि आपने कुछ चीजों को अपना मन खोला है जिसके बारे में मैंने सोचा नहीं है। –

11

मेरे अनुभव में:

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

अपने पूरे करियर में, क्या आपके पास कभी ऐसी स्थिति है जहां कक्षा (या विधि) को वास्तव में 10 (!) पैरामीटर लेना पड़ा? –

+8

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

+0

मुझे बहुत सारे कोड दिखाई देते हैं जो श्रेणी 2 में आते हैं, टालने योग्य और टालना चाहिए। यदि मैंने कभी एक श्रेणी 3 से मुलाकात की तो इसे अधिक दबाव वाले मुद्दों के एक बड़े ढेर के नीचे फटकारा गया था, इसलिए इसे छोड़ने का कारण था। लेकिन मैं वास्तव में एक असली दुनिया प्रणाली से एक श्रेणी 1 देखना पसंद करेंगे। –

3

हां - इस पैरामीटर को लेने वाली विधि को कोड गंध माना जाना चाहिए। क्या यह विधि वास्तव में केवल एक चीज और एक चीज कर रही है?

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

कुल्ला और दोहराएं जब तक कि समेकन अब और समझ में नहीं आता है और/या आपके पास उचित पैरामीटर हैं। "Refactoring to Aggregate Services"

+0

कृपया मेरा संपादन देखें। –

-2

मैं भी आपके आवेदन नया स्वरूप की सलाह देंगे:

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

public interface IAbstractContainer 
{ 
    T Resolve<T>(); 
} 

public class ConcreteContainer: IAbstractContainer 
{ 
    private IContainer _container; // E.g. Autofac container 

    public ConcreteContainer(IContainer container) 
    { 
    _container = container; 
    { 

    public T Resolve<T>() 
    { 
    return _container.Resolve<T>(); 
    } 
} 

public classA(IAbstractContainer container) 
{ 
    this.B = container.Resolve<ClassB>(); 
    this.C = container.Resolve<ClassC>(); 
    ... 
} 

}

एक ConcreteContainer उदाहरण हमेशा की तरह इंजेक्ट किया जाता है।

+3

यह समस्या का समाधान नहीं करता है, यह सब निर्भरता को "छुपाएं" है क्योंकि आप अब कन्स्ट्रक्टर इंजेक्शन का उपयोग नहीं कर रहे हैं, इसके अलावा आपके पास कंटेनर की नई निर्भरता है - वास्तव में यह आईओसी नहीं है लेकिन सेवा लोकेटर पैटर्न – BrokenGlass

+3

का उपयोग करना सेवा लोकेटर एक अच्छा विचार नहीं है क्योंकि DI के कई (लाभ) लाभों में से एक यह है कि आपको कुछ विचार है कि ऑब्जेक्ट की निर्भरता क्या है। –

+3

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

संबंधित मुद्दे