NInject का उपयोग करके फैक्ट्री बनाने का सबसे अच्छा तरीका क्या है?


27

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

अब मैं एक विंडोज़ एप्लिकेशन विकसित करना शुरू कर रहा हूं, मैं एप्लीकेशन वाइड डिपेंडेंसी इंजेक्शन का उपयोग करना चाहता हूं। यानी हर वस्तु को NInject के माध्यम से बनाया जाना चाहिए, ताकि यूनिट टेस्टिंग में आसानी हो। कृपया मुझे यह सुनिश्चित करने के लिए मार्गदर्शन करें कि बनाई गई प्रत्येक वस्तु केवल NInject Factory की ही होनी चाहिए।

उदाहरण के लिए, अगर Button_Clickघटना पर कोई भी विंडोज़ फॉर्म मैं लिखता हूं:

TestClass testClass = new TestClass()

और TestClassइस पर कोई निर्भरता है, ITestतो यह स्वचालित रूप से हल किया जाना चाहिए। मुझे पता है मैं उपयोग कर सकता हूं:

Ikernel kernel = new StandardKenel()
//AddBinding()
TestClass testClass = kenel.get<TestClass>();

लेकिन मुझे हर बार ऐसा लगता है कि मैं एक वस्तु बनाना चाहता हूं। यह ऑब्जेक्ट डेवलपर को एक विशेष तरीके से ऑब्जेक्ट बनाने के लिए भी करता है। क्या इसे बेहतर बनाया जा सकता है?

क्या मेरे पास वस्तु निर्माण के लिए एक केंद्रीय भंडार हो सकता है और फिर प्रत्येक वस्तु निर्माण स्वचालित रूप से उस भंडार का उपयोग करेगा?


1
हाय प्रवीण पाटिल: महान प्रश्न। मैंने आपके शीर्षक के लिए एक मामूली बदलाव किया और यह स्पष्ट किया कि आप क्या पूछ रहे हैं; यदि मैं चूक गया तो संशोधित करने में संकोच न करें।

@ मर्कट्रैप: उपयुक्त शीर्षक के लिए धन्यवाद। मुझे वह टैगलाइन याद आ गई ...
प्रवीण पाटिल

लघु पक्ष नोट के रूप में, इस परियोजना को "निन्जेक्ट" कहा जाता है, न कि "एनइजेक्ट"। हालांकि यह हो सकता है कि यह एन-इंजेक्ट रहा हो, वे आजकल नौ-जेए थीम पर खेलते हैं। :) Cf. Ninject.org
कॉर्नेलियस

जवाबों:


12

क्लाइंट अनुप्रयोगों के लिए, अक्सर एमवीपी (या एमवीवीएम) जैसे पैटर्न को अनुकूलित करना और फॉर्म-व्यू-इन करके अंतर्निहित ViewModel या प्रस्तुतकर्ता का उपयोग करना सबसे अच्छा होता है।

ViewModels के लिए, आप मानक कन्स्ट्रक्टर इंजेक्शन का उपयोग करके आवश्यक निर्भरता को इंजेक्ट कर सकते हैं।

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


7

Windows प्रपत्र अनुप्रयोगों में आमतौर पर प्रविष्टि का एक बिंदु होता है जो इस तरह दिखता है:

    // Program.cs
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MainForm());
    }

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

    // Program.cs
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        var kernel = InitializeNinjectKernel();
        Application.Run(kernel.Get<MainForm>());
    }

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

public MainForm(TestClass testClass) {
    _testClass = testClass;
}

यदि आपकी "निर्भरता" ऐसी चीज है जिसे आपको कई बार उत्पादन करने में सक्षम होना चाहिए, तो आपको वास्तव में क्या चाहिए एक कारखाना है:

public MainForm(IFactory<TestClass> testClassFactory) {
    _testClassFactory = testClassFactory;
}

...
var testClass = _testClassFactory.Get();

आप एकतरफा कार्यान्वयन की एक टन बनाने से बचने के लिए इस तरह से इफैक्टीव इंटरफ़ेस लागू कर सकते हैं:

public class InjectionFactory<T> : IFactory<T>, IObjectFactory<T>, IDependencyInjector<T>
{
    private readonly IKernel _kernel;
    private readonly IParameter[] _contextParameters;

    public InjectionFactory(IContext injectionContext)
    {
        _contextParameters = injectionContext.Parameters
            .Where(p => p.ShouldInherit).ToArray();
        _kernel = injectionContext.Kernel;
    }

    public T Get()
    {
        try
        {
            return _kernel.Get<T>(_contextParameters.ToArray());
        }
        catch (Exception e)
        {
            throw new Exception(
                string.Format("An error occurred while attempting to instantiate an object of type <{0}>",
                typeof(T)));
        }
    }

...
Bind(typeof (IFactory<>)).To(typeof (InjectionFactory<>));
Bind(typeof (IContext)).ToMethod(c => c.Request.ParentContext);

कृपया, क्या आपके पास इस कारखाने के लिए पूर्ण कार्यान्वयन है?
Tebo

@ColourBlend: नहीं, लेकिन अगर आपको मेरे द्वारा InjectionFactory<T>लागू किए गए अन्य इंटरफेस से छुटकारा मिलता है , तो प्रदान किया गया कोड आपको ठीक काम करना चाहिए। विशेष रूप से कुछ है कि आप के साथ मुद्दों कर रहे हैं?
स्ट्रिपिंगवर्यर

मैंने पहले ही इसे लागू कर दिया था, मैं सिर्फ यह जानना चाहता हूं कि क्या कक्षा के भीतर अन्य दिलचस्प चीजें थीं।
Tebo

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

4

मैं हमेशा किसी भी IoC कंटेनर के लिए एक एडॉप्टर आवरण लिखता हूं, जो इस तरह दिखता है:

public static class Ioc
{
    public static IIocContainer Container { get; set; }
}

public interface IIocContainer 
{
    object Get(Type type);
    T Get<T>();
    T Get<T>(string name, string value);
    void Inject(object item);
    T TryGet<T>();
}

Ninject के लिए, विशेष रूप से, ठोस एडेप्टर वर्ग इस तरह दिखता है:

public class NinjectIocContainer : IIocContainer
{
    public readonly IKernel Kernel;
    public NinjectIocContainer(params INinjectModule[] modules) 
    {
        Kernel = new StandardKernel(modules);
        new AutoWirePropertyHeuristic(Kernel);
    }

    private NinjectIocContainer()
    {
        Kernel = new StandardKernel();
        Kernel.Load(AppDomain.CurrentDomain.GetAssemblies());

        new AutoWirePropertyHeuristic(Kernel);
    }

    public object Get(Type type)
    {
        try
        {
            return Kernel.Get(type);
        }
        catch (ActivationException exception)
        {
            throw new TypeNotResolvedException(exception);
        }              
    }

    public T TryGet<T>()
    {
        return Kernel.TryGet<T>();
    }

    public T Get<T>()
    {
        try
        {
            return Kernel.Get<T>();
        }
        catch (ActivationException exception)
        {
            throw new TypeNotResolvedException(exception);
        }           
    }

    public T Get<T>(string name, string value)
    {
        var result = Kernel.TryGet<T>(metadata => metadata.Has(name) &&
                     (string.Equals(metadata.Get<string>(name), value,
                                    StringComparison.InvariantCultureIgnoreCase)));

        if (Equals(result, default(T))) throw new TypeNotResolvedException(null);
            return result;
    }

    public void Inject(object item)
    {
        Kernel.Inject(item);
    }
}

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

लेकिन, एक बोनस के रूप में, अन्य ढाँचों के अंदर IoC ढांचे का उपयोग करने के लिए चीजें बहुत आसान हो जाती हैं जो स्वाभाविक रूप से इसका समर्थन नहीं करते हैं। WinForms के लिए, उदाहरण के लिए, यह दो चरण हैं:

अपनी मुख्य विधि में, बस कुछ भी करने से पहले एक कंटेनर को तुरंत हटा दें।

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        try
        {
            Ioc.Container = new NinjectIocContainer( /* include modules here */ );
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MyStartupForm());
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
    }
}

और फिर एक आधार फॉर्म होता है, जिसमें से अन्य रूप निकाले जाते हैं, जो खुद को इंजेक्ट कहता है।

public IocForm : Form
{
    public IocForm() : base()
    {
        Ioc.Container.Inject(this);
    }
}

यह ऑटो-वायरिंग हेयुरिस्टिक को उस रूप में सभी गुणों को पुन: इंजेक्ट करने का प्रयास करने के लिए कहता है जो आपके मॉड्यूल में स्थापित नियमों को फिट करते हैं।


बहुत अच्छा समाधान ..... मैं इसे आजमाऊंगा
प्रवीण पाटिल

10
यह एक सेवा लोकेटर है, जो एक बहुत बुरा विचार है: blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx
Mark Seemann

2
@ मार्किस्मैन: एक सेवा लोकेटर एक बुरा विचार है, यदि आप इसे हर जगह से एक्सेस करते हैं, तो इसे अपने शीर्ष स्तर की वस्तुओं को जहां तक ​​हो सके नीचे तक तार देने दें। मार्क की अपनी टिप्पणी पढ़ें, पेज को थोड़ा नीचे करें: "ऐसे मामलों में आपके पास वास्तव में कोई सहारा नहीं है, लेकिन प्रत्येक ऑब्जेक्ट (जैसे पेज) में कंपोजीशन रूट को स्थानांतरित करने के लिए और अपने डीआई कंटेनर को वहां से अपनी निर्भरता तार दें। यह ऐसा लग सकता है। सेवा लोकेटर विरोधी पैटर्न, लेकिन यह नहीं है क्योंकि आप अभी भी एक न्यूनतम पर कंटेनर का उपयोग करते हैं। " (संपादित करें: रुको, आप मार्क हैं! तो क्या अंतर है?)
पीडीआर '

1
अंतर यह है कि आप अभी भी किसी भी वर्ग के लिए सिंगलटन सर्विस लोकेटर उपलब्ध कराने के बजाय, अपने बाकी कोड आधार को कंपोज़र से ढाल सकते हैं।
मार्क सीमेन

2
@pdr: मेरे अनुभव में, यदि आप विशेषता वर्ग जैसी चीजों में सेवाओं को इंजेक्ट करने का प्रयास कर रहे हैं, तो आप चिंताओं को ठीक से अलग नहीं कर रहे हैं। ऐसे मामले हैं जहां आप जिस फ्रेमवर्क का उपयोग कर रहे हैं, वह उचित निर्भरता इंजेक्शन का उपयोग करने के लिए व्यावहारिक रूप से असंभव बनाता है, और कभी-कभी हम एक सेवा लोकेटर का उपयोग करने के लिए मजबूर होते हैं, लेकिन मैं निश्चित रूप से इसे वापस करने से पहले जहां तक ​​संभव हो सच्चे डीआई लेने की कोशिश करूंगा। पैटर्न।
स्ट्रिपिंगवर्यर 23

1

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

तो इसे ठीक करने के बारे में दो तरीके हैं:

  1. उन उदाहरणों को इंजेक्ट करें जिन्हें एक वर्ग की आवश्यकता होगी। अपने उदाहरण में, TestClassअपने विंडोज फॉर्म में इंजेक्ट करें ताकि इसकी आवश्यकता होने पर पहले से ही एक उदाहरण हो। जब Ninject आपके फॉर्म को तत्काल तैयार करता है, तो यह स्वचालित रूप से निर्भरता बनाता है।
  2. ऐसे मामलों में जहां तुम सच में नहीं है एक उदाहरण जब तक आप इसे जरूरत है बनाना चाहते हैं, आप व्यापार तर्क में एक कारखाने इंजेक्षन कर सकते हैं। उदाहरण के लिए, आप IKernelअपने विंडोज फॉर्म में इंजेक्ट कर सकते हैं , और फिर उसे इंस्टेंट करने के लिए उपयोग कर सकते हैं TestClass। आपकी शैली के आधार पर, इसे पूरा करने के अन्य तरीके भी हैं (फैक्ट्री क्लास, फैक्ट्री डेलीगेट इत्यादि को इंजेक्ट करना)।

ऐसा करने से टेस्टक्लास के दोनों ठोस प्रकारों की अदला-बदली करना आसान हो जाता है, साथ ही परीक्षण वर्ग के वास्तविक निर्माण को संशोधित करता है, वास्तव में बिना कोड को संशोधित किए जो परीक्षण वर्ग का उपयोग करता है।


1

मैं Ninject का उपयोग नहीं किया है, लेकिन सामान बनाने जब आप एक आईओसी उपयोग कर रहे हैं मानक तरीका एक के माध्यम से ऐसा करने के लिए है Func<T>, जहां Tवस्तु के प्रकार आप बनाना चाहते हैं है। इसलिए यदि ऑब्जेक्ट T1को टाइप की वस्तुएं बनाने की आवश्यकता है, T2तो कंस्ट्रक्टर के T1पास टाइप का पैरामीटर होना चाहिए, Func<T1>जिसे बाद में फ़ील्ड / प्रॉपर्टी के रूप में संग्रहीत किया जाता है T2। अब आप प्रकार की वस्तुओं का निर्माण करना चाहते हैं जब T2में T1आह्वान आप Func

यह आपको अपने आईओसी ढांचे से पूरी तरह से अलग करता है और आईओसी मानसिकता में कोड करने का सही तरीका है।

ऐसा करने के लिए नकारात्मक पक्ष यह है कि यह कष्टप्रद हो सकता है जब आपको मैन्युअल रूप से वायर Funcएस या उदाहरण की आवश्यकता होती है जब आपके निर्माता को कुछ पैरामीटर की आवश्यकता होती है, इसलिए आईओसी Funcआपके लिए ऑटोवॉयर नहीं कर सकता है ।

http://code.google.com/p/autofac/wiki/RelationshipTypes

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