Tytuł mówi „Zależność kołowa”, ale nie jest to właściwe sformułowanie, ponieważ dla mnie konstrukcja wydaje się solidna.
Zastanów się jednak nad następującym scenariuszem, w którym niebieskie części podano od partnera zewnętrznego, a pomarańczowy to moja własna implementacja. Załóżmy również, że jest więcej niż jeden ConcreteMain
, ale chcę użyć określonego. (W rzeczywistości każda klasa ma kilka innych zależności, ale próbowałem to uprościć tutaj)
Chciałbym zaimplementować to wszystko za pomocą Deprence Injection (Unity), ale oczywiście otrzymuję StackOverflowException
następujący kod, ponieważ Runner próbuje utworzyć instancję ConcreteMain, a ConcreteMain potrzebuje Runnera.
IUnityContainer ioc = new UnityContainer();
ioc.RegisterType<IMain, ConcreteMain>()
.RegisterType<IMainCallback, Runner>();
var runner = ioc.Resolve<Runner>();
Jak mogę tego uniknąć? Czy jest jakiś sposób, aby to ustrukturyzować, aby móc używać go z DI? Scenariusz, który teraz tworzę, polega na ręcznym konfigurowaniu wszystkiego, ale nakłada to na ConcreteMain
klasę silną zależność, która go tworzy . Właśnie tego próbuję uniknąć (z rejestracjami Unity w konfiguracji).
Cały kod źródłowy poniżej (bardzo uproszczony przykład!);
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));
}
}