परिवेश संदर्भ बनाम निर्माता इंजेक्शन


9

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

मेरे पास दो कब्ज़ेदार कार्यान्वयन हैं। मुख्य वर्ग जो सभी तीनों वर्गों के साथ IAmbientContext को स्वीकार करता है या सभी तीन वर्गों के लिए इंजेक्शन लगाता है।

public interface ISessionContext 
{
    ...
}

public class MySessionContext: ISessionContext 
{
    ...
}

public interface ILogManager 
{

}

public class MyLogManager: ILogManager 
{
    ...
}

public interface IService 
{
    ...
}

public class MyService: IService
{
    ...
}

पहला उपाय:

public class AmbientContext
{
    private ISessionContext sessionContext;
    private ILogManager logManager;
    private IService service;

    public AmbientContext(ISessionContext sessionContext, ILogManager logManager, IService service)
    {
        this.sessionContext = sessionContext;
        this.logManager = logManager;
        this.service = service;
    }
}


public class MyCoreClass(AmbientContext ambientContext)
{
    ...
}

दूसरा समाधान (बिना परिवेश के)

public MyCoreClass(ISessionContext sessionContext, ILogManager logManager, IService service)
{
    ...
}

विच इस मामले में सबसे अच्छा समाधान है?


" IServiceअन्य सेवाओं के साथ संचार के लिए क्या उपयोग किया जाता है?" यदि IServiceअन्य सेवाओं पर अस्पष्ट निर्भरता का प्रतिनिधित्व करता है तो यह एक सेवा लोकेटर की तरह लगता है और इसका अस्तित्व नहीं होना चाहिए। आपकी कक्षा को उन इंटरफेस पर निर्भर होना चाहिए जो स्पष्ट रूप से वर्णन करते हैं कि उनके उपभोक्ता उनके साथ क्या करेंगे। किसी वर्ग को कभी भी किसी सेवा तक पहुँच प्रदान करने के लिए किसी सेवा की आवश्यकता नहीं होती है। एक वर्ग को एक निर्भरता की आवश्यकता होती है जो कुछ विशिष्ट करता है जो वर्ग को चाहिए।
स्कॉट हैनन

जवाबों:


4

"बेस्ट" यहाँ बहुत व्यक्तिपरक है। जैसा कि इस तरह के फैसलों के साथ होता है, यह कुछ हासिल करने के दो समान तरीकों के बीच एक व्यापार-बंद है।

यदि आप एक बनाते हैं AmbientContextऔर इंजेक्ट करते हैं कि कई वर्गों में, आप संभावित रूप से उनमें से प्रत्येक को अधिक जानकारी प्रदान कर रहे हैं, जैसे कि उन्हें जरूरत है ( Fooकेवल वर्ग ही उपयोग कर सकता है ISessionContext, लेकिन इसके बारे में ILogManagerऔर ISessionभी बताया जा रहा है)।

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

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


4

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

क्या होगा यदि ISessionउदाहरण का जीवनकाल एक से भिन्न हो ILogger? हो सकता है कि हर बार अनुरोध और ILogger पर ISession उदाहरण बनाया जाए। इसलिए ये सभी निर्भरताएँ एक वस्तु द्वारा नियंत्रित होती हैं जो कि स्वयं कंटेनर नहीं है, जीवन भर प्रबंधन और स्थानीयकरण और इस धागे में वर्णित अन्य लोगों के साथ इन सभी समस्याओं के कारण सही विकल्प नहीं दिखता है।

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

तो सबसे आसान तरीका है क्रॉस कटिंग निर्भरता से निपटने के लिए कंस्ट्रक्टर इंजेक्शन या किसी अन्य इंजेक्शन तंत्र का उपयोग नहीं करना, लेकिन एक स्थिर कॉल का उपयोग करना । हम वास्तव में इस पैटर्न को काफी बार देखते हैं, जिसे फ्रेमवर्क द्वारा ही कार्यान्वित किया जाता है। Thread.CurrentPrincipal की जाँच करें जो कि एक स्थैतिक गुण है जो IPrincipalइंटरफ़ेस का कार्यान्वयन करता है। आप इसे पसंद करने पर कार्यान्वयन को बदल सकते हैं, इसलिए यह भी निपटान योग्य है, इस प्रकार आप इसे करने के लिए युग्मित नहीं हैं।

MyCore अब कुछ ऐसा दिखता है

public class MyCoreClass
{
    public void BusinessFeature(string data)
    {
        LoggerContext.Current.Log(data);

        _repository.SaveProcessedData();

        SessionContext.Current.SetData(data);
        ...etc
    }
}

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

आप से बचना चाहते हैं AmbientContext.Current.Logger, AmbientContext.Current.Sessionइसी कारण के रूप में ऊपर वर्णित है।

लेकिन आपके पास इस समस्या को हल करने के लिए अन्य विकल्प हैं: सज्जाकार, गतिशील अवरोधन का उपयोग करें यदि आपके कंटेनर में यह क्षमता या एओपी है। एम्बिएंट कॉनटेक्स्ट अंतिम उपाय होना चाहिए क्योंकि इस तथ्य के कारण इसके ग्राहक इसके माध्यम से अपनी निर्भरता छिपाते हैं। मैं अभी भी परिवेश संदर्भ का उपयोग करूंगा यदि यह इंटरफ़ेस वास्तव में मेरे आवेग का उपयोग करता है जैसे कि एक स्थिर निर्भरता का उपयोग करने के लिए DateTime.Nowया ConfigurationManager.AppSettingsयह बहुत बार उठता है। लेकिन अंत में कंस्ट्रक्टर इंजेक्शन इन सर्वव्यापी निर्भरता प्राप्त करने के लिए ऐसा बुरा विचार नहीं हो सकता है।


3

मैं इससे बचूंगा AmbientContext

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

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

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

चौथा, इसमें सामंजस्य का अभाव है और एकल जिम्मेदारी सिद्धांत का उल्लंघन करता है। इसलिए इसका एक नाम "AmbientContext" है, क्योंकि यह बहुत सारी असंबंधित चीजों को करता है और इसके अनुसार इसका नाम रखने का कोई तरीका नहीं है।

और यह शायद इंटरफ़ेस सदस्यों को उन कक्षाओं में पेश करके इंटरफ़ेस अलगाव सिद्धांत का उल्लंघन करता है जिनकी उन्हें आवश्यकता नहीं है।


2

दूसरा (इंटरफ़ेस आवरण के बिना)

जब तक विभिन्न सेवाओं के बीच कुछ बातचीत नहीं होती है, जो एक मध्यवर्ती वर्ग में इनकैप्सुलेटिंग की आवश्यकता होती है, यह केवल आपके कोड को जटिल करता है और जब आप 'इंटरफेस का इंटरफेस' शुरू करते हैं तो लचीलापन सीमित करता है।

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