डिपेंडेंसी इंजेक्शन कंस्ट्रक्टर पागलपन से कैसे बचें?


300

मुझे लगता है कि मेरे निर्माता इस तरह दिखना शुरू कर रहे हैं:

public MyClass(Container con, SomeClass1 obj1, SomeClass2, obj2.... )

कभी बढ़ती पैरामीटर सूची के साथ। चूंकि "कंटेनर" मेरा निर्भरता इंजेक्शन कंटेनर है, मैं ऐसा क्यों नहीं कर सकता:

public MyClass(Container con)

हर वर्ग के लिए? डाउनसाइड क्या हैं? यदि मैं ऐसा करता हूं, तो ऐसा लगता है कि मैं एक शानदार स्थैतिक का उपयोग कर रहा हूं। कृपया आईओसी और डिपेंडेंसी इंजेक्शन पागलपन पर अपने विचार साझा करें।


64
आप कंटेनर क्यों पास कर रहे हैं? मुझे लगता है कि आप
पॉल क्रीसी

33
यदि आपके निर्माता अधिक या अधिक मापदंडों की मांग कर रहे हैं, तो आप उन कक्षाओं में बहुत अधिक कर रहे हैं।
ऑस्टिन सलोनन

38
यही नहीं आप कंस्ट्रक्टर इंजेक्शन कैसे करते हैं। ऑब्जेक्ट्स IoC कंटेनर के बारे में बिल्कुल नहीं जानते हैं, न ही उन्हें।
duffymo

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

जवाबों:


409

आप सही कह रहे हैं कि यदि आप कंटेनर का उपयोग सेवा लोकेटर के रूप में करते हैं, तो यह कम या ज्यादा एक शानदार स्थैतिक कारखाना है। बहुत सारे कारणों से मैं इसे एक विरोधी पैटर्न मानता हूं

कंस्ट्रक्टर इंजेक्शन के अद्भुत लाभों में से एक यह है कि यह एकल जिम्मेदारी सिद्धांत के उल्लंघन को स्पष्ट रूप से स्पष्ट करता है।

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


8
एकल अवधारणा में रीफैक्टरिंग प्रयास को बढ़ाने के लिए +1; भयानक :)
रयान एमरेल

46
सच में? आपने केवल उन मापदंडों को एक अन्य वर्ग में स्थानांतरित करने के लिए एक अप्रत्यक्ष बनाया, लेकिन वे अभी भी वहां हैं! उनसे निपटने के लिए बस अधिक जटिल है।
अप्रतिबंधित

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

92
सबसे अच्छी टिप्पणी: "कंस्ट्रक्टर इंजेक्शन के अद्भुत लाभों में से एक यह है कि यह सिंगल रिस्पॉन्सिबिलिटी प्रिंसिपल फ्रीजर के उल्लंघन को स्पष्ट करता है।"
इगोर पोपोव

2
@ डोनबॉक्स उस स्थिति में आप पुनरावृत्ति को रोकने के लिए अशक्त वस्तु कार्यान्वयन लिख सकते हैं। आपकी जरूरत नहीं है, लेकिन मुद्दा यह है कि कंस्ट्रक्टर इंजेक्शन चक्र को रोकता नहीं है - यह केवल यह स्पष्ट करता है कि वे वहां हैं।
मार्क सेमैन

66

मुझे नहीं लगता कि आपके क्लास कंस्ट्रक्टर्स को आपके IOC कंटेनर की अवधि का संदर्भ होना चाहिए। यह आपकी कक्षा और कंटेनर के बीच एक अनावश्यक निर्भरता का प्रतिनिधित्व करता है (निर्भरता का प्रकार IOC से बचने की कोशिश कर रहा है!)।


+1 IoC कंटेनर के आधार पर उस कंटेनर को अन्य सभी वर्गों में कोड के w / o बदलते गुच्छा पर बाद में बदलना कठिन बनाता है
Tseng

1
आप रचनाकारों पर इंटरफ़ेस मापदंडों के बिना IOC कैसे लागू करेंगे? क्या मैं आपकी पोस्ट को गलत पढ़ रहा हूँ?
जे हंट

@ जे हंट मैं आपकी टिप्पणी नहीं समझता। मेरे लिए, इंटरफ़ेस पैरामीटर्स का मतलब उन मापदंडों से है, जो निर्भरता के लिए इंटरफेस हैं, यानी, यदि आपकी निर्भरता इंजेक्शन कंटेनर इनिशियलाइज़ करता है MyClass myClass = new MyClass(IDependency1 interface1, IDependency2 interface2)(इंटरफ़ेस पैरामीटर)। यह @ व्युत्पत्ति के पद से असंबंधित है, जो मैं कहता हूं कि एक निर्भरता इंजेक्शन कंटेनर को अपनी वस्तुओं में खुद को इंजेक्ट नहीं करना चाहिए, अर्थातMyClass myClass = new MyClass(this)
जॉन डो

25

मापदंडों में पारित होने की कठिनाई समस्या नहीं है। समस्या यह है कि आपकी कक्षा बहुत अधिक कर रही है, और अधिक टूट जाना चाहिए।

निर्भरता इंजेक्शन बहुत बड़ी हो रही कक्षाओं के लिए एक प्रारंभिक चेतावनी के रूप में कार्य कर सकता है, विशेष रूप से निर्भरता के सभी में गुजरने के बढ़ते दर्द के कारण।


43
अगर मैं गलत हूं, तो मुझे सुधारें, लेकिन कुछ बिंदुओं पर आपको 'सब कुछ एक साथ गोंद' करना होगा, और इस तरह आपको उसके लिए कुछ निर्भरताएं प्राप्त करनी होंगी। उदाहरण के लिए, दृश्य परत में, जब टेम्पलेट और उनके लिए डेटा का निर्माण करते हैं, तो आपको सभी डेटा को विभिन्न निर्भरताओं (जैसे 'सेवाओं') से हथियाना होगा और फिर इस सारे डेटा को टेम्पलेट और स्क्रीन पर रखना होगा। यदि मेरे वेब पेज में सूचना के 10 अलग-अलग 'ब्लॉक' हैं, तो मुझे उस डेटा के साथ प्रदान करने के लिए 10 अलग-अलग वर्गों की आवश्यकता है। तो मुझे अपने View / Template Class में 10 निर्भरताएँ चाहिए?
एंड्रयू

4

मुझे कंस्ट्रक्टर आधारित निर्भरता इंजेक्शन के बारे में एक समान प्रश्न आया था और यह सभी आश्रितों में पास होने के लिए कितना जटिल था।

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

स्पष्टीकरण के साथ विस्तृत कोड यहां पाया जा सकता है

जटिल सेवा परत में आईओसी के लिए सर्वोत्तम अभ्यास


3

मैंने इस पूरे धागे को दो बार पढ़ा, और मुझे लगता है कि लोग जो जानते हैं उसका जवाब दे रहे हैं, जो नहीं पूछा गया है।

जेपी का मूल प्रश्न ऐसा लगता है कि वह एक रिसॉल्वर भेजकर वस्तुओं का निर्माण कर रहा है, और फिर कक्षाओं का एक समूह है, लेकिन हम यह मान रहे हैं कि वे वर्ग / वस्तुएं स्वयं सेवाएं हैं, इंजेक्शन के लिए परिपक्व हैं। अगर वे नहीं हैं तो क्या होगा?

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

Container.GetSevice<MyClass>(someObject1, someObject2)

... इस प्रारूप का समर्थन शायद ही कभी किया जाता है। मेरा मानना ​​है कि इस तरह के समर्थन की प्रोग्रामिंग की कठिनाई, दुखी प्रदर्शन में जोड़ा गया जो कार्यान्वयन के साथ जुड़ा होगा, यह ओपनसोर्स डेवलपर्स के लिए अनाकर्षक बनाता है।

लेकिन यह किया जाना चाहिए, क्योंकि मुझे MyClass'es के लिए एक कारखाना बनाने और पंजीकृत करने में सक्षम होना चाहिए, और उस कारखाने को डेटा / इनपुट प्राप्त करने में सक्षम होना चाहिए जिसे केवल "सेवा" के रूप में पारित करने के लिए धक्का नहीं दिया गया है। डेटा। यदि "एंटी-पैटर्न" नकारात्मक परिणामों के बारे में है, तो डेटा / मॉडल पास करने के लिए कृत्रिम सेवा प्रकारों के अस्तित्व को मजबूर करना निश्चित रूप से नकारात्मक है (अपने वर्गों को कंटेनर में लपेटने के बारे में आपकी भावना के बराबर)।

ऐसे ढाँचे हैं, जो मदद कर सकते हैं, हालाँकि, भले ही वे थोड़े बदसूरत दिखते हों। उदाहरण के लिए, Ninject:

कंस्ट्रक्टर में अतिरिक्त मापदंडों के साथ Ninject का उपयोग करके एक उदाहरण बनाना

यह .NET के लिए लोकप्रिय है, और अभी भी उतना ही साफ है जितना इसे होना चाहिए, लेकिन मुझे यकीन है कि आप जिस भी भाषा में काम करना चाहते हैं उसमें कुछ है।


3

कंटेनर को इंजेक्ट करना एक शॉर्टकट है जिसे आप अंततः पछतावा करेंगे।

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

यहाँ बाहर की चीजों की एक अपूर्ण सूची है

खराब डोमेन डिज़ाइन (सकल रूट की…। आदि)

चिंताओं की खराब जुदाई (सेवा संरचना, कमांड, प्रश्न) CQRS और इवेंट सोर्सिंग देखें।

या मैपर (सावधान रहें, ये चीजें आपको परेशानी में डाल सकती हैं)

मॉडल और अन्य डीटीओ देखें (एक का पुन: उपयोग न करें, और उन्हें कम से कम रखने की कोशिश करें !!!!)


2

मुसीबत :

1) कभी बढ़ती पैरामीटर सूची के साथ कंस्ट्रक्टर।

2) यदि वर्ग विरासत में मिला है (Ex:) RepositoryBaseतो निर्माणकर्ता हस्ताक्षर बदलने से व्युत्पन्न वर्गों में परिवर्तन होता है।

समाधान 1

IoC Containerकंस्ट्रक्टर को पास

क्यों

  • कोई और अधिक बढ़ती पैरामीटर सूची
  • कंस्ट्रक्टर का हस्ताक्षर सरल हो जाता है

क्यों नहीं

  • आपको आईओसी कंटेनर में कसकर युग्मित वर्ग बनाता है। (यह समस्या तब पैदा होती है जब 1. आप उस क्लास को अन्य प्रोजेक्ट्स में उपयोग करना चाहते हैं जहाँ आप विभिन्न IoC कंटेनर का उपयोग करते हैं। आप IoC कंटेनर बदलने का निर्णय लेते हैं)
  • आपको कम वर्णनात्मक बनाता है। (आप वास्तव में क्लास कंस्ट्रक्टर को नहीं देख सकते हैं और कह सकते हैं कि इसे कामकाज के लिए क्या चाहिए।)
  • कक्षा संभावित रूप से सभी सेवा का उपयोग कर सकती है।

समाधान २

एक क्लास बनाएं जो सभी सर्विस को ग्रुप करे और कंस्ट्रक्टर को पास करे

 public abstract class EFRepositoryBase 
 {
    public class Dependency
    {
        public DbContext DbContext { get; }
        public IAuditFactory AuditFactory { get; }

         public Dependency(
            DbContext dbContext,
            IAuditFactory auditFactory)
        {
            DbContext = dbContext;
            AuditFactory = auditFactory;
        }
    }

    protected readonly DbContext DbContext;        
    protected readonly IJobariaAuditFactory auditFactory;

    protected EFRepositoryBase(Dependency dependency)
    {
        DbContext = dependency.DbContext;
        auditFactory= dependency.JobariaAuditFactory;
    }
  }

व्युत्पन्न वर्ग

  public class ApplicationEfRepository : EFRepositoryBase      
  {
     public new class Dependency : EFRepositoryBase.Dependency
     {
         public IConcreteDependency ConcreteDependency { get; }

         public Dependency(
            DbContext dbContext,
            IAuditFactory auditFactory,
            IConcreteDependency concreteDependency)
        {
            DbContext = dbContext;
            AuditFactory = auditFactory;
            ConcreteDependency = concreteDependency;
        }
     }

      IConcreteDependency _concreteDependency;

      public ApplicationEfRepository(
          Dependency dependency)
          : base(dependency)
      { 
        _concreteDependency = dependency.ConcreteDependency;
      }
   }

क्यों

  • वर्ग में नई निर्भरता जोड़ने से व्युत्पन्न वर्ग प्रभावित नहीं होते हैं
  • वर्ग IoC कंटेनर का अज्ञेयवादी है
  • वर्ग वर्णनात्मक है (इसकी निर्भरता के पहलू में)। सम्मेलन द्वारा, यदि आप जानना चाहते हैं कि कौन सी कक्षा Aनिर्भर करती है, तो वह जानकारी संचित हो जाती हैA.Dependency
  • कंस्ट्रक्टर हस्ताक्षर सरल हो जाता है

क्यों नहीं

  • अतिरिक्त वर्ग बनाने की आवश्यकता है
  • सेवा पंजीकरण जटिल हो जाता है (आपको X.Dependencyअलग से पंजीकरण करने की आवश्यकता होती है )
  • वैचारिक रूप से गुजरने के समान IoC Container
  • ..

समाधान 2 सिर्फ एक कच्चा है, हालांकि अगर इसके खिलाफ ठोस तर्क है, तो वर्णनात्मक टिप्पणी की सराहना की जाएगी


1

यह मेरे द्वारा उपयोग किया जाने वाला दृष्टिकोण है

public class Hero
{

    [Inject]
    private IInventory Inventory { get; set; }

    [Inject]
    private IArmour Armour { get; set; }

    [Inject]
    protected IWeapon Weapon { get; set; }

    [Inject]
    private IAction Jump { get; set; }

    [Inject]
    private IInstanceProvider InstanceProvider { get; set; }


}

यहाँ एक क्रूड दृष्टिकोण है कि इंजेक्शन कैसे करें और मूल्यों को इंजेक्ट करने के बाद कंस्ट्रक्टर कैसे चलाएं। यह पूरी तरह कार्यात्मक कार्यक्रम है।

public class InjectAttribute : Attribute
{

}


public class TestClass
{
    [Inject]
    private SomeDependency sd { get; set; }

    public TestClass()
    {
        Console.WriteLine("ctor");
        Console.WriteLine(sd);
    }
}

public class SomeDependency
{

}


class Program
{
    static void Main(string[] args)
    {
        object tc = FormatterServices.GetUninitializedObject(typeof(TestClass));

        // Get all properties with inject tag
        List<PropertyInfo> pi = typeof(TestClass)
            .GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
            .Where(info => info.GetCustomAttributes(typeof(InjectAttribute), false).Length > 0).ToList();

        // We now happen to know there's only one dependency so we take a shortcut just for the sake of this example and just set value to it without inspecting it
        pi[0].SetValue(tc, new SomeDependency(), null);


        // Find the right constructor and Invoke it. 
        ConstructorInfo ci = typeof(TestClass).GetConstructors()[0];
        ci.Invoke(tc, null);

    }
}

मैं वर्तमान में एक हॉबी प्रोजेक्ट पर काम कर रहा हूँ जो इस तरह से काम करता है https://github.com/Jokine/ToolProject/tree/Tore


-8

आप किस निर्भरता इंजेक्शन ढांचे का उपयोग कर रहे हैं? क्या आपने इसके बजाय सेटर आधारित इंजेक्शन का उपयोग करने की कोशिश की है?

कंस्ट्रक्टर आधारित इंजेक्शन का लाभ यह है कि यह जावा प्रोग्रामर के लिए स्वाभाविक लगता है जो DI फ्रेमवर्क का उपयोग नहीं करते हैं। क्लास को इनिशियलाइज़ करने के लिए आपको 5 चीजों की आवश्यकता होती है फिर आपके कंस्ट्रक्टर के लिए 5 तर्क होते हैं। नकारात्मक पक्ष यह है कि आपने क्या देखा है, यह तब निर्भर करता है जब आपके पास बहुत सारी निर्भरताएं होती हैं।

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

दोनों तरीके काम करते हैं, यह वरीयता का मामला है।


21
कंस्ट्रक्टर इंजेक्शन का कारण निर्भरता को स्पष्ट करना है, इसलिए नहीं कि यह जावा डेवलपर्स के लिए अधिक स्वाभाविक लगता है।
एल-फोर

8
देर से टिप्पणी, लेकिन इस जवाब ने मुझे हँसा दिया :)
फ्रेडरिक प्रेज

1
सेटर आधारित इंजेक्शन के लिए +1। अगर मेरी सेवाएं और रिपॉजिटरी मेरी कक्षा में परिभाषित हैं तो यह स्पष्ट है कि वे निर्भरताएं हैं .. मुझे बड़े पैमाने पर VB6 देखने वाले निर्माणकर्ता लिखने की ज़रूरत नहीं है, और निर्माणकर्ता में बेवकूफ असाइनिंग कोड है। यह बहुत स्पष्ट है कि क्या निर्भरता आवश्यक क्षेत्रों पर है।
पायोत्र कुला

2018 के अनुसार, स्प्रिंग आधिकारिक तौर पर सेटर इंजेक्शन का उपयोग नहीं करने की सलाह देते हैं, जो उन पर निर्भरता को छोड़कर उचित डिफ़ॉल्ट मान रखते हैं। जैसे, यदि निर्भरता वर्ग के लिए अनिवार्य है, तो कंस्ट्रक्टर इंजेक्शन की सिफारिश की जाती है। सेटर बनाम ctor DI
जॉन डो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.