हाल ही में मुझे यह लेख मिला है: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.1। यह ASP.NET कोर एप्लिकेशन में निर्भरता इंजेक्शन के बारे में है। वहाँ एक सिफारिश है, कि मैं वास्तव में समझ नहीं पा रहा हूँ:
"GetService का उपयोग करके रनटाइम पर निर्भरता को हल करने वाली फ़ैक्टरी को इंजेक्ट करने से बचें।"

समस्या यह है कि ऐसा दृष्टिकोण उपयोगी हो सकता है। मान लीजिए कि हमें रनटाइम के दौरान N के कुछ के उदाहरण बनाने हैं। इसलिए हम किसी फैक्ट्री को क्लाइंट क्लास में इंजेक्ट कर सकते हैं, जो उस फैक्ट्री का इस्तेमाल कुछ बनाने के लिए करेगा। लेकिन क्या होगा अगर कुछ को भी हल करने के लिए कुछ निर्भरताएं हैं? इसका मतलब है कि हमें सभी निर्भरताओं को हल करने के लिए हमारे कारखाने में GetService का उपयोग करना होगा। लेकिन इस तरह से हम इस विरोधी पैटर्न का इस्तेमाल करेंगे, जिससे हमें बचना चाहिए।

तो मेरा सवाल है: हमें स्पष्ट रूप से GetService को कॉल करने से क्यों बचना चाहिए, और हम कुछ इसी तरह कैसे लागू कर सकते हैं, लेकिन बिना GetService के?

1
Daniil Ryzhkov 29 पद 2020, 13:10

2 जवाब

सबसे बढ़िया उत्तर

Microsoft दस्तावेज़ीकरण यह सलाह देने में गलत नहीं है, बल्कि यह सरल है क्योंकि इसमें बहुत अधिक संदर्भ का अभाव है। मैं वास्तव में लेखकों को दोष नहीं देता, क्योंकि इस विषय पर पूरी किताबें लिखी जा सकती हैं (जो कि बिल्कुल वही है जो मैंने किया था)।

विचार करने के लिए बहुत कुछ है। समझने के लिए एक महत्वपूर्ण पहली अवधारणा है, रचना रूट। यह उस एप्लिकेशन का हिस्सा है जहां निर्भरता पंजीकृत होती है, और ऑब्जेक्ट ग्राफ़ का निर्माण होता है।

एक अन्य महत्वपूर्ण अवधारणा है सर्विस लोकेटर एंटी-पैटर्न। जैसा कि एंटी-पैटर्न लेबल का तात्पर्य है, यह एक ऐसा पैटर्न है जिससे हमेशा बचना चाहिए। जब आप किसी भी सेवा को हल करने के लिए कोड (जो कंपोजिशन रूट के बाहर रहता है) को अनुमति देते हैं तो आप इस विरोधी पैटर्न को लागू कर रहे हैं। यह मामला होगा, उदाहरण के लिए, जब आप किसी उपभोक्ता में Func<Type, object> प्रतिनिधि को इंजेक्ट करते हैं, या जब आप किसी उपभोक्ता में IServiceProvider इंजेक्ट करते हैं। इसकी GetService विधि में वह प्रतिनिधि है।

कंपोजिशन रूट के अंदर रहने वाले कोड के लिए, दूसरी ओर, सर्विस लोकेटर की कोई धारणा नहीं है; यह विरोधी पैटर्न वहां मौजूद नहीं है। ऐसा इसलिए है क्योंकि कंपोजीशन रूट का प्राथमिक कार्य प्रकार बनाना है। इसकी पहले से ही DI कंटेनर पर सीधी निर्भरता है। कंपोजिशन रूट से सेवाओं के समाधान की उम्मीद की जाती है।

हालांकि, यह नोट करना महत्वपूर्ण है कि एक विशिष्ट प्रतिनिधि का होना सेवा लोकेटर के समान नहीं है। उदाहरण के लिए, यदि आप एक Func<IMyService> इंजेक्ट करते हैं, तो यह सेवा लोकेटर नहीं है और इसलिए, प्रति दृश्य एक बुरा विचार नहीं है।

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

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

लेकिन भले ही उपभोक्ता अपनी निर्भरता को स्टेटलेस के रूप में देख सकते हैं, हो सकता है कि निर्भरता का कार्यान्वयन न हो। इसका अभी भी मतलब है कि इन निर्भरताओं को किसी बिंदु पर बनाया जाना चाहिए। और यदि आप उनके उपभोक्ताओं को ऐसा करने की अनुमति नहीं देते हैं, तो उन्हें कौन बना सकता है?

इस प्रश्न का उत्तर है: रचना जड़। ज्यादातर मामलों में, आपको निर्भरता के निर्माण को एप्लिकेशन कोड से बाहर और कंपोजिशन रूट में स्थानांतरित करना चाहिए।

उदाहरण के लिए इस HomeController को लें, जिसे IProductRepository बनाने के लिए एक कारखाने में इंजेक्ट किया जाता है:

public class HomeController
{
    private Func<IProductRepository> repoFactory;

    public HomeController(Func<IProductRepository> repoFactory)
    {
        this.repoFactory = repoFactory;
    }

    public ViewResult Index()
    {
        var repository = this.repoFactory.Invoke();
        var products = repository.GetFeaturedProducts();
        return this.View(products);
    }
}

HomeController के दृष्टिकोण से विचार करने के लिए दो निर्भरताएँ हैं: Func<IProducerRepository> और IProductRepository। यह जटिलता की ओर जाता है। HomeController के दृष्टिकोण से, एक सरल समाधान निम्नलिखित होगा:

public class HomeController
{
    private IProductRepository repository;

    public HomeController(IProductRepository repository)
    {
        this.repository = repository;
    }

    public ViewResult Index()
    {
        var products = this.repository.GetFeaturedProducts();
        return this.View(products);
    }
}

हालांकि यह HomeController को सरल करता है, यह समस्या को इधर-उधर करता है। लेकिन इस जटिलता को कंपोजिशन रूट में ले जाने का विचार है।

उदाहरण के लिए, कंपोजिशन रूट के अंदर, आप निम्नलिखित कार्यान्वयन बना सकते हैं:

public class ProductRepositoryProxy : IProductRepository
{
    private Func<IProductRepository> repoFactory;

    public ProductRepositoryProxy(Func<IProductRepository> repoFactory)
    {
        this.repoFactory = repoFactory;
    }

    public IEnumerable<Product> GetFeaturedProducts()
    {
        var repository = this.repoFactory.Invoke();
        return repository.GetFeaturedProducts();
    }
}

यह ProductRepositoryProxy मूल Func<IProductRepository> को लपेटता है और प्रत्येक कॉल पर इसे आमंत्रित करता है।

यह ProductRepositoryProxy मानता है कि कोई भी IProductRepository कार्यान्वयन स्टेटफुल है और अल्पकालिक होना चाहिए। और यह ठीक है क्योंकि यह ज्ञान अब कंपोजिशन रूट में केंद्रीकृत है। यह सरल करता है शेष एप्लिकेशन अब व्यावसायिक निर्णयों पर ध्यान केंद्रित कर सकता है और निर्भरता प्रबंधन के बारे में कम।

यह विवरण सार कारखानों का दुरुपयोग पर एक लंबी चर्चा का एक छोटा संस्करण है, जो मेरी किताब.

0
Steven 29 पद 2020, 18:56

मुझे आशा है कि मैं आपको सही ढंग से समझ रहा हूँ।

मान लें कि आपके पास 10 कक्षाएं हैं जो ISomething लागू करती हैं। आप उन्हें इस तरह अपने सेवा चयन के साथ पंजीकृत कर सकते हैं:

services.AddTransient<ISomething, Something1>();
services.AddTransient<ISomething, Something2>();

इस तरह आपके कार्यान्वयन में निर्भरता भी हो सकती है लेकिन उन्हें आपके DI कंटेनर के माध्यम से इंजेक्ट किया जाता है।

यदि आप अपनी कक्षा में सभी ISomething कार्यान्वयन को इंजेक्ट करना चाहते हैं, तो बस उन्हें अपने कंस्ट्रक्टर के माध्यम से इंजेक्ट करें।

public class SomethingConsumer
{
    public SomethingConsumer(IEnumerable<ISomthing> somethingList)
    {
    }
    ...
}
-1
Dharman 29 पद 2020, 13:34