Il titolo dice "Dipendenza circolare", ma non è la formulazione corretta, perché per me il design sembra solido.
Tuttavia, considera il seguente scenario, in cui le parti blu sono fornite da un partner esterno e arancione è la mia implementazione. Supponi anche che ce ne sia più di uno ConcreteMain
, ma voglio usarne uno specifico. (In realtà, ogni classe ha alcune dipendenze in più, ma ho provato a semplificarlo qui)
Vorrei istanziare tutto questo con Depency Injection (Unity), ma ovviamente ottengo un StackOverflowException
codice sul seguente codice, perché Runner cerca di creare un'istanza di ConcreteMain e ConcreteMain ha bisogno di un Runner.
IUnityContainer ioc = new UnityContainer();
ioc.RegisterType<IMain, ConcreteMain>()
.RegisterType<IMainCallback, Runner>();
var runner = ioc.Resolve<Runner>();
Come posso evitarlo? C'è un modo per strutturarlo in modo che io possa usarlo con DI? Lo scenario che sto facendo ora sta impostando tutto manualmente, ma questo ConcreteMain
crea una forte dipendenza nella classe che lo crea un'istanza. Questo è ciò che sto cercando di evitare (con le registrazioni di Unity nella configurazione).
Tutto il codice sorgente di seguito (esempio molto semplificato!);
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));
}
}