कैसे निर्भरता इंजेक्शन में "परिपत्र निर्भरता" को संभालने के लिए


16

शीर्षक "सर्कुलर डिपेंडेंसी" कहता है, लेकिन यह सही शब्द नहीं है, क्योंकि मेरे लिए डिज़ाइन ठोस लगता है।
हालांकि, निम्नलिखित परिदृश्य पर विचार करें, जहां बाहरी हिस्से से नीले रंग के हिस्से दिए गए हैं, और नारंगी मेरा अपना कार्यान्वयन है। इसके अलावा मान लें कि एक और है ConcreteMain, लेकिन मैं एक विशिष्ट का उपयोग करना चाहता हूं। (वास्तव में, प्रत्येक वर्ग की कुछ और निर्भरताएँ होती हैं, लेकिन मैंने इसे यहाँ सरल बनाने की कोशिश की है)

परिदृश्य

मैं डिप्रेशन इंजेक्शन (एकता) के साथ इस सब को भड़काना चाहूंगा, लेकिन मुझे स्पष्ट रूप StackOverflowExceptionसे निम्नलिखित कोड मिलता है, क्योंकि रनर कंक्रीटमैने को तुरंत करने की कोशिश करता है, और कंक्रीटमैन को एक रनर की आवश्यकता होती है।

IUnityContainer ioc = new UnityContainer();
ioc.RegisterType<IMain, ConcreteMain>()
   .RegisterType<IMainCallback, Runner>();
var runner = ioc.Resolve<Runner>();

मैं इसे कैसे प्राप्त कर सकता हूं? क्या इसकी संरचना का कोई तरीका है ताकि मैं इसे DI के साथ उपयोग कर सकूं? अब मैं जो परिदृश्य कर रहा हूं, वह सब कुछ मैन्युअल रूप से सेट कर रहा है, लेकिन यह ConcreteMainउस वर्ग पर एक कठिन निर्भरता रखता है जो इसे तुरंत करता है। यही मैं बचने की कोशिश कर रहा हूं (कॉन्फ़िगरेशन में एकता पंजीकरण के साथ)।

नीचे सभी स्रोत कोड (बहुत सरल उदाहरण!);

public class Program
{
    public static void Main(string[] args)
    {
        IUnityContainer ioc = new UnityContainer();
        ioc.RegisterType<IMain, ConcreteMain>()
           .RegisterType<IMainCallback, Runner>();
        var runner = ioc.Resolve<Runner>();

        Console.WriteLine("invoking runner...");
        runner.DoSomethingAwesome();

        Console.ReadLine();
    }
}

public class Runner : IMainCallback
{
    private readonly IMain mainServer;

    public Runner(IMain mainServer)
    {
        this.mainServer = mainServer;
    }

    public void DoSomethingAwesome()
    {
        Console.WriteLine("trying to do something awesome");
        mainServer.DoSomething();
    }

    public void SomethingIsDone(object something)
    {
        Console.WriteLine("hey look, something is finally done.");
    }
}

public interface IMain
{
    void DoSomething();
}

public interface IMainCallback
{
    void SomethingIsDone(object something);
}

public abstract class AbstractMain : IMain
{
    protected readonly IMainCallback callback;

    protected AbstractMain(IMainCallback callback)
    {
        this.callback = callback;
    }

    public abstract void DoSomething();
}

public class ConcreteMain : AbstractMain
{
    public ConcreteMain(IMainCallback callback) : base(callback){}

    public override void DoSomething()
    {
        Console.WriteLine("starting to do something...");
        var task = Task.Factory.StartNew(() =>{ Thread.Sleep(5000);/*very long running task*/ });
        task.ContinueWith(t => callback.SomethingIsDone(true));
    }
}

जवाबों:


10

फैक्ट्री बनाने के लिए आप क्या कर सकते हैं, मेनफैक्ट्री जो कि कंक्रीट के एक उदाहरण को आईनैन लौटाता है।

फिर आप इस फैक्ट्री को अपने रनर कंस्ट्रक्टर में इंजेक्ट कर सकते हैं। कारखाने के साथ मुख्य बनाएँ और खुद को एक पैरामीटर के रूप में पास करें।

कंकरीट निर्माता पर किसी भी अन्य निर्भरता को आईओसी के माध्यम से MyMainFactory में पारित किया जा सकता है और मैन्युअल रूप से कंक्रीट निर्माणकर्ता को धकेल दिया जा सकता है।

public class MyMainFactory
{
    MyOtherDependency _dependency;

    public MyMainFactory(MyOtherDependency dependency)
    {
        _dependency = dependency;
    }

    public IMain Create(Runner runner)
    {
        return new ConcreteMain(runner, _dependency);
    }
}

public class Runner
{
    IMain _myMain;
    public Runner(MyMainFactory factory)
    {
        _myMain = factory.Create(this)
    }
}

4

इस परिदृश्य का समर्थन करने वाले IOC कंटेनर का उपयोग करें। मुझे पता है कि AutoFac और संभव अन्य करता है। AutoFac का उपयोग करते समय प्रतिबंध यह है कि निर्भरता में से किसी एक के पास PropertiesAutoWired = true होना चाहिए और निर्भरता के लिए एक संपत्ति का उपयोग करना चाहिए।


4

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


4

यूनिटी 3 के साथ, अब आप इंजेक्ट कर सकते हैं Lazy<T>। यह फैक्ट्री / ऑब्जेक्ट कैश को इंजेक्ट करने के समान है।

बस सुनिश्चित करें कि आप अपने ctor में काम नहीं करते हैं जिसमें आलसी निर्भरता को हल करने की आवश्यकता होती है।

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