आपको उन समस्याओं को हल करने के लिए तकनीकों का उपयोग करना चाहिए जो आपके पास उन समस्याओं को हल करने में अच्छी हैं। निर्भरता उलटा और इंजेक्शन अलग नहीं हैं।
निर्भरता उलटा या इंजेक्शन एक तकनीक है जो आपके कोड को यह तय करने की अनुमति देती है कि किसी पद्धति के कार्यान्वयन को रन टाइम पर क्या कहा जाता है। यह देर से बाध्यकारी के लाभों को अधिकतम करता है। जब भाषा गैर-इंस्टेंस कार्यों के रन टाइम प्रतिस्थापन का समर्थन नहीं करती है तो तकनीक आवश्यक है। उदाहरण के लिए, जावा में कॉल को किसी भिन्न कार्यान्वयन के साथ स्थिर विधि में कॉल करने के लिए एक तंत्र की कमी होती है; पाइथन के विपरीत, जहां फ़ंक्शन कॉल को बदलने के लिए सभी आवश्यक है नाम को एक अलग फ़ंक्शन में बाँधना है (फ़ंक्शन को पकड़े हुए चर को पुन: असाइन करें)।
हम फ़ंक्शन के कार्यान्वयन को अलग क्यों करना चाहते हैं? इसके दो मुख्य कारण हैं:
- हम परीक्षण उद्देश्यों के लिए फेक का उपयोग करना चाहते हैं। यह हमें एक ऐसे वर्ग का परीक्षण करने की अनुमति देता है जो वास्तव में डेटाबेस से जुड़े बिना एक डेटाबेस फ़ेच पर निर्भर करता है।
- हमें कई कार्यान्वयन का समर्थन करने की आवश्यकता है। उदाहरण के लिए, हमें MySQL और PostgreSQL डेटाबेस का समर्थन करने वाली प्रणाली स्थापित करने की आवश्यकता हो सकती है।
आप नियंत्रण कंटेनरों के विलोम पर भी ध्यान देना चाहते हैं। यह एक ऐसी तकनीक है जिसका उद्देश्य इस विशालकाय पेड़ की तरह दिखने वाले विशाल, पेचीदा निर्माण वाले पेड़ों से बचने में आपकी मदद करना है:
thing5 = new MyThing5();
thing3 = new MyThing3(thing5, new MyThing10());
myApp = new MyApp(
new MyAppDependency1(thing5, thing3),
new MyAppDependency2(
new Thing1(),
new Thing2(new Thing3(thing5, new Thing4(thing5)))
),
...
new MyAppDependency15(thing5)
);
यह आपको अपनी कक्षाएं पंजीकृत करने देता है और फिर आपके लिए निर्माण करता है:
injector.register(Thing1); // Yes, you'd need some kind of actual class reference.
injector.register(Thing2);
...
injector.register(MyAppDepdency15);
injector.register(MyApp);
myApp = injector.create(MyApp); // The injector fills in all the construction parameters.
ध्यान दें कि यह सबसे सरल है यदि पंजीकृत कक्षाएं स्टेटलेस सिंगललेट हो सकती हैं ।
सावधानी का शब्द
ध्यान दें कि निर्भरता व्युत्क्रम तर्क-वितर्क के लिए उत्तर देने के लिए आपका जाना नहीं होना चाहिए । इसके बजाय पैरामीटराइज़ेशन का उपयोग करने के अवसरों की तलाश करें । उदाहरण के लिए इस छद्म विधि पर विचार करें:
myAverageAboveMin()
{
dbConn = new DbConnection("my connection string");
dbQuery = dbConn.makeQuery();
dbQuery.Command = "SELECT * FROM MY_DATA WHERE x > :min";
dbQuery.setParam("min", 5);
dbQuery.Execute();
myData = dbQuery.getAll();
count = 0;
total = 0;
foreach (row in myData)
{
count++;
total += row.x;
}
return total / count;
}
हम इस पद्धति के कुछ हिस्सों के लिए निर्भरता व्युत्क्रम का उपयोग कर सकते हैं:
class MyQuerier
{
private _dbConn;
MyQueries(dbConn) { this._dbConn = dbConn; }
fetchAboveMin(min)
{
dbQuery = this._dbConn.makeQuery();
dbQuery.Command = "SELECT * FROM MY_DATA WHERE x > :min";
dbQuery.setParam("min", min);
dbQuery.Execute();
return dbQuery.getAll();
}
}
class Averager
{
private _querier;
Averager(querier) { this._querier = querier; }
myAverageAboveMin(min)
{
myData = this._querier.fetchAboveMin(min);
count = 0;
total = 0;
foreach (row in myData)
{
count++;
total += row.x;
}
return total / count;
}
लेकिन हमें कम से कम पूरी तरह से नहीं होना चाहिए। ध्यान दें कि हमने एक स्टेटफुल क्लास बनाई है Querier
। यह अब कुछ अनिवार्य रूप से वैश्विक कनेक्शन ऑब्जेक्ट के लिए एक संदर्भ रखता है। इससे कार्यक्रम की समग्र स्थिति को समझने में कठिनाई होती है और विभिन्न वर्ग एक दूसरे के साथ समन्वय कैसे करते हैं। ध्यान दें कि यदि हम औसत तर्क का परीक्षण करना चाहते हैं, तो हमें क्वायर या कनेक्शन को नकली करने के लिए मजबूर किया जाता है। इसके अलावा एक बेहतर दृष्टिकोण को बढ़ाने के लिए किया जाएगा parameterization :
class MyQuerier
{
fetchAboveMin(dbConn, min)
{
dbQuery = dbConn.makeQuery();
dbQuery.Command = "SELECT * FROM MY_DATA WHERE x > :min";
dbQuery.setParam("min", min);
dbQuery.Execute();
return dbQuery.getAll();
}
}
class Averager
{
averageData(myData)
{
count = 0;
total = 0;
foreach (row in myData)
{
count++;
total += row.x;
}
return total / count;
}
class StuffDoer
{
private _querier;
private _averager;
StuffDoer(querier, averager)
{
this._querier = querier;
this._averager = averager;
}
myAverageAboveMin(dbConn, min)
{
myData = this._querier.fetchAboveMin(dbConn, min);
return this._averager.averageData(myData);
}
}
और कनेक्शन को कुछ और भी उच्च स्तर पर प्रबंधित किया जाएगा जो एक पूरे के रूप में ऑपरेशन के लिए जिम्मेदार है और जानता है कि इस आउटपुट के साथ क्या करना है।
अब हम क्वेरी के पूरी तरह से स्वतंत्र रूप से औसत तर्क का परीक्षण कर सकते हैं, और व्यापक स्थितियों में हम इसका अधिक उपयोग कर सकते हैं। हम सवाल कर सकते हैं कि क्या हमें वस्तुओं MyQuerier
और Averager
वस्तुओं की भी आवश्यकता है , और शायद इसका उत्तर यह है कि यदि हम इकाई परीक्षण का इरादा नहीं रखते हैं, तो नहीं StuffDoer
और इकाई परीक्षण StuffDoer
पूरी तरह से उचित नहीं होगा क्योंकि यह डेटाबेस में बहुत कसकर युग्मित है। यह सिर्फ एकीकरण परीक्षण इसे कवर करने के लिए और अधिक समझ में आता है। उस स्थिति में, हम ठीक बनाने fetchAboveMin
और averageData
स्थिर तरीकों में हो सकते हैं ।