निर्भरता इंजेक्शन के लिए धीरे-धीरे दृष्टिकोण


12

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

एक दृष्टिकोण जो मैं बता रहा हूं, वह सभी "नए" कॉल को अपने तरीकों से आगे बढ़ा रहा है, जैसे:

public MyObject createMyObject(args) {
  return new MyObject(args);
}

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

क्या यह एक अच्छा तरीका है? क्या कोई नुकसान हैं?

अधिक आम तौर पर, जब तक आप उन्हें परीक्षण के लिए बदल सकते हैं, तब तक हार्ड-कोडित निर्भरताएं रखना ठीक है? मुझे पता है कि पसंदीदा दृष्टिकोण स्पष्ट रूप से उन्हें कंस्ट्रक्टर में आवश्यक है, और मैं वहां अंततः प्राप्त करना चाहता हूं। लेकिन मैं सोच रहा हूं कि क्या यह एक अच्छा पहला कदम है।

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


क्या आप इस बारे में विस्तार से बता सकते हैं कि वे इकाई परीक्षण योग्य क्यों नहीं हैं?
ऑस्टिन सलोनन

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

जवाबों:


13

यह आपको आरंभ करने के लिए एक अच्छा तरीका है। ध्यान दें कि इकाई परीक्षणों के साथ अपने मौजूदा कोड को कवर करना सबसे महत्वपूर्ण है; एक बार जब आपके पास परीक्षण होते हैं, तो आप अपने डिज़ाइन को और बेहतर बनाने के लिए अधिक स्वतंत्र रूप से रिफ्लेक्टर कर सकते हैं

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

इस विषय पर पढ़ने के लिए मौलिक काम विरासत कोड के साथ प्रभावी ढंग से काम कर रहा है । यह आपके द्वारा ऊपर दिखाए गए ट्रिक का वर्णन करता है और कई, कई और, कई भाषाओं का उपयोग करता है।


इस पर एक टिप्पणी "आपके पास एक बार परीक्षण करने के बाद, आप अधिक स्वतंत्र रूप से रिफ्लेक्टर कर सकते हैं" - यदि आपके पास परीक्षण नहीं हैं, तो आप रिफैक्टिंग नहीं कर रहे हैं, आप बस कोड बदल रहे हैं।
ओसोडो

@Slomojo मैं आपकी बात देख रहा हूं, लेकिन आप अभी भी परीक्षणों के बिना बहुत सारे सुरक्षित रीफैक्टरिंग कर सकते हैं, विशेष रूप से स्वचालित आईडीई रेक्टोरिंग जैसे, उदाहरण के लिए (जावा के लिए ग्रहण में) तरीकों से डुप्लिकेट कोड निकालकर, सब कुछ बदलकर, चलती कक्षाएं और स्थाई क्षेत्र, स्थिरांक आदि
अलब

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

1
@ एलएलबी, बिना परीक्षण के रिफैक्टरिंग करना हमेशा अधिक जोखिम भरा होता है। स्वचालित रिफ्लेक्टर सुनिश्चित करते हैं कि जोखिम कम हो जाता है, लेकिन शून्य तक नहीं। हमेशा ट्रिकी एज केस होते हैं जो हो सकता है कि टूल सही तरीके से हैंडल न कर पाएं। बेहतर स्थिति में परिणाम उपकरण से कुछ त्रुटि संदेश है, लेकिन सबसे खराब स्थिति में यह चुपचाप सूक्ष्म कीड़े का परिचय दे सकता है। तो यह सलाह दी जाती है कि स्वचालित साधनों के साथ भी सबसे सरल और सुरक्षित संभव परिवर्तनों को रखें।
Péter Török

3

गिटार-लोग उपयोग करने की सलाह देते हैं

@Inject
public void setX(.... X x) {
   this.x = x;
}

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


3

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

तो मेरी नई इकाई के परीक्षण योग्य वर्ग में 2 निर्माता होंगे:

public MyClass() { 
  this(new Client1(), new Client2()); 
}

public MyClass(Client1 client1, Client2 client2) { 
  this.client1 = client1; 
  this.client2 = client2; 
}

2

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

उदाहरण के लिए,

public class Example {
  private MyObject myObject=null;

  public MyObject getMyObject() {
    if (myObject==null) {
      myObject=new MyObject();
    } 
    return myObject;
  }

  public void setMyObject(MyObject myObject) {
    this.myObject=myObject;
  }

  private void myMethod() {
    if (getMyObject().doSomething()) {
      // Instance automatically created
    }
  }
}

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

उपरोक्त कोड केवल एक उदाहरण है और यह सीधे आंतरिक स्थिति को उजागर करता है। आपको ध्यान से विचार करना चाहिए कि क्या यह दृष्टिकोण आपके कोडबेस के लिए उपयुक्त है।

मैं यह भी सलाह दूंगा कि आप " वर्किंग इफ़ेक्टली विद लिगेसी कोड " पढ़ते हैं जिसमें एक प्रमुख रीफैक्टरिंग की सफलता के लिए उपयोगी युक्तियों का खजाना है।


धन्यवाद। मैं कुछ मामलों के लिए इस दृष्टिकोण पर विचार कर रहा हूं। लेकिन आप क्या करते हैं जब MyObject के निर्माता को उन मापदंडों की आवश्यकता होती है जो केवल आपकी कक्षा के अंदर से उपलब्ध हैं? या जब आपको अपनी कक्षा के जीवनकाल के दौरान एक से अधिक MyObject बनाने की आवश्यकता होती है? क्या आपको एक MyObjectFactory इंजेक्षन करना है?
JW01

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

यह एक सिंगलटन है। बस
सिंगलेट्स

@DarioOO वास्तव में यह एक बहुत ही गरीब सिंगलटन है। एक बेहतर कार्यान्वयन जोश बलोच के अनुसार एक एनम का उपयोग करेगा। सिंगलेट्स एक वैध डिजाइन पैटर्न हैं और उनके उपयोग (महंगी एक-बंद निर्माण आदि) हैं।
गैरी रोवे
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.