Bağımlılık enjeksiyonunda “dairesel bağımlılık” nasıl ele alınır


16

Başlık "Dairesel Bağımlılık" diyor, ancak doğru ifade değil, çünkü bana göre tasarım sağlam görünüyor.
Bununla birlikte, mavi parçaların dış ortaktan verildiği ve portakalın kendi uygulamam olduğu aşağıdaki senaryoyu düşünün. Aynı zamanda birden fazla olduğunu varsayalım ConcreteMain, ama belirli bir tane kullanmak istiyorum. (Gerçekte, her sınıfın biraz daha bağımlılıkları var, ama burada basitleştirmeye çalıştım)

senaryo

Tüm bunları Depency Injection (Unity) ile başlatmak istiyorum, ancak Açıkçası StackOverflowExceptionaşağıdaki kodda bir tane alıyorum , çünkü Runner ConcreteMain'i başlatmaya çalışıyor ve ConcreteMain'in bir Runner'a ihtiyacı var.

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

Bundan nasıl kaçınabilirim? Bunu DI ile kullanabilmem için yapılandırmanın bir yolu var mı? Şu anda yaptığım senaryo her şeyi manuel olarak kuruyor, ancak bu ConcreteMainonu başlatan sınıfta sert bir bağımlılık yaratıyor. (Yapılandırmada Unity kayıtları ile) kaçınmaya çalışıyorum budur.

Aşağıdaki tüm kaynak kodları (çok basitleştirilmiş örnek!);

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));
    }
}

Yanıtlar:


10

Yapabileceğiniz şey, IMain olarak ConcreteMain örneğini döndüren bir fabrika, MainFactory oluşturmaktır.

Daha sonra bu Fabrikayı Runner yapıcısına enjekte edebilirsiniz. Main'i fabrika ile oluşturun ve inn'in kendisini parametre olarak iletin.

ConcreteMain yapıcısındaki diğer bağımlılıklar IOC aracılığıyla MyMainFactory'ye aktarılabilir ve elle beton kurucuya itilebilir.

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

Bu senaryoyu destekleyen bir IOC kapsayıcısı kullanın. AutoFac ve olası diğerlerinin yaptığını biliyorum. AutoFac kullanırken kısıtlama, bağımlılıklardan birinin PropertiesAutoWired = true değerine sahip olması ve bağımlılık için bir Özellik kullanmasıdır.


4

Bazı IOC kapları (örneğin Yay veya Kaynak) dinamik olarak oluşturulan proxy'ler kullanarak bu sorunu çözebilir. Proxy'ler her iki uçta enjekte edilir ve gerçek nesne yalnızca proxy ilk kullanıldığında başlatılır. Bu şekilde, iki nesne kendi yapıcılarında (kaçınılması kolay) yöntemleri çağırmadıkça dairesel bağımlılıklar bir sorun oluşturmaz.


4

Unity 3 ile artık enjekte edebilirsiniz Lazy<T>. Bu, bir Fabrika / nesne önbelleği enjekte etmeye benzer.

Tembel bağımlılığın çözülmesini gerektiren kasanızda çalışmadığınızdan emin olun.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.