Il titolo dice "Dipendenza circolare", ma non è la dicitura corretta, perché per me il design sembra solido.
Tuttavia, si consideri il seguente scenario, in cui le parti blu sono fornite da un partner esterno e l'arancione è la mia implementazione. Supponiamo anche che ci sia più di un ConcreteMain
, ma voglio usare uno specifico. (In realtà, ogni classe ha alcune dipendenze in più, ma ho provato a semplificarla qui)
VorreiinstallaretuttoquestoconDepencyInjection(Unity),maovviamenteottengounStackOverflowException
sulseguentecodice,perchéRunnertentadiistanziareConcreteMaineConcreteMainhabisognodiunRunner.
IUnityContainerioc=newUnityContainer();ioc.RegisterType<IMain,ConcreteMain>().RegisterType<IMainCallback,Runner>();varrunner=ioc.Resolve<Runner>();
Comepossoevitarequesto?C'èunmodoperstrutturarequestoinmodocheiopossausarloconDI?Loscenariochestofacendoorastaimpostandotuttomanualmente,maciòponeunastrongdipendenzadaConcreteMain
nellaclassecheloistanzia.Questoèquellochestocercandodievitare(conleregistrazionidiUnityinconfigurazione).
Tuttoilcodicesorgentesotto(esempiomoltosemplificato!);
publicclassProgram{publicstaticvoidMain(string[]args){IUnityContainerioc=newUnityContainer();ioc.RegisterType<IMain,ConcreteMain>().RegisterType<IMainCallback,Runner>();varrunner=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));
}
}